AQS同步組件--ReentrantLock與鎖

ReentrantLock與鎖

Synchronized和ReentrantLock異同
  • 可重入性:二者都具備可重入性
  • 鎖的實現:Synchronized是依賴jvm實現的,ReentrantLock是jdk實現的。(咱們能夠理解爲一個是操做系統層面的實現另外一個是用戶本身本身實現的)Synchronized的實現是jvm層面的很難看到其中的實現。而ReentrantLock是經過jvm實現的咱們能夠經過閱讀jvm源碼來查看實現。
  • 性能區別:在Synchronized優化以前Synchronized的性能相比ReentrantLock差不少,在Synchronized引入了偏向鎖,輕量級鎖也就是自旋鎖以後了,二者的性能相差不大了。在二者均可用的狀況下官方更推薦使用Synchronized,由於其寫法更簡單,Synchronized的優化就是借鑑了ReentrantLock中的cas技術。
  • 功能區別:便利性,很明顯synchronized的使用更加便利,ReentrantLock在細粒度和靈活性中會優於Synchronized。
ReentrantLock獨有功能
  • ReentrantLock可指定是公平鎖仍是非公平鎖,Synchronized只能是非公平鎖。(公平鎖就是先等待的線程先得到鎖)
  • ReentrantLock提供一個Condition類,能夠分組喚醒須要喚醒的造成。synchronized是要嘛隨機喚醒一個線程要嘛喚醒全部的線程。
  • ReentrantLock提供了一種可以中斷等待鎖的線程的機制lock.locInterruptibly(),ReentrantLock實現是一種自旋鎖經過循環調用,經過cas機制來實現加鎖。性能較好是由於避免了線程進入內核的阻塞狀態
@Slf4j
public class LockExample2 {

    // 請求總數
    public static int clientTotal = 5000;

    // 同時併發執行的線程數
    public static int threadTotal = 200;

    public static int count = 0;

    private final static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    private static void add() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

咱們首先使用 private final static Lock lock = new ReentrantLock()聲明一個所得實例,而後使用java

lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }

進行加鎖和解鎖操做。併發

咱們在經過一個例子來看看這個ReentrantReadWriteLock怎麼用。jvm

@Slf4j
public class LockExample3 {

    private final Map<String, Data> map = new TreeMap<>();

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private final Lock readLock = lock.readLock();

    private final Lock writeLock = lock.writeLock();

    public Data get(String key) {
        readLock.lock();
        try {
            return map.get(key);
        } finally {
            readLock.unlock();
        }
    }

    public Set<String> getAllKeys() {
        readLock.lock();
        try {
            return map.keySet();
        } finally {
            readLock.unlock();
        }
    }

    public Data put(String key, Data value) {
        writeLock.lock();
        try {
            return map.put(key, value);
        } finally {
            readLock.unlock();
        }
    }

    class Data {

    }
}

經過 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock()聲明一個ReentrantReadWriteLock,而後再分別獲取 private final Lock readLock = lock.readLock() private final Lock writeLock = lock.writeLock()讀鎖和寫鎖。
咱們在這個map讀的時候加上讀鎖在寫的時候加上寫鎖,可是這裏有問題就是這個鎖是悲觀鎖,也就是說在執行寫鎖的時候必定不能有讀鎖,當讀操做特 特別多的時候頗有可能會讓寫鎖一直沒法執行。性能

咱們看一下官方的例子學習一下,StampedLock學習

import java.util.concurrent.locks.StampedLock;

public class LockExample4 {

    class Point {
        private double x, y;
        private final StampedLock sl = new StampedLock();

        void move(double deltaX, double deltaY) { // an exclusively locked method
            long stamp = sl.writeLock();
            try {
                x += deltaX;
                y += deltaY;
            } finally {
                sl.unlockWrite(stamp);
            }
        }

        //下面看看樂觀讀鎖案例
        double distanceFromOrigin() { // A read-only method
            long stamp = sl.tryOptimisticRead(); //得到一個樂觀讀鎖
            double currentX = x, currentY = y;  //將兩個字段讀入本地局部變量
            if (!sl.validate(stamp)) { //檢查發出樂觀讀鎖後同時是否有其餘寫鎖發生?
                stamp = sl.readLock();  //若是沒有,咱們再次得到一個讀悲觀鎖
                try {
                    currentX = x; // 將兩個字段讀入本地局部變量
                    currentY = y; // 將兩個字段讀入本地局部變量
                } finally {
                    sl.unlockRead(stamp);
                }
            }
            return Math.sqrt(currentX * currentX + currentY * currentY);
        }

        //下面是悲觀讀鎖案例
        void moveIfAtOrigin(double newX, double newY) { // upgrade
            // Could instead start with optimistic, not read mode
            long stamp = sl.readLock();
            try {
                while (x == 0.0 && y == 0.0) { //循環,檢查當前狀態是否符合
                    long ws = sl.tryConvertToWriteLock(stamp); //將讀鎖轉爲寫鎖
                    if (ws != 0L) { //這是確認轉爲寫鎖是否成功
                        stamp = ws; //若是成功 替換票據
                        x = newX; //進行狀態改變
                        y = newY;  //進行狀態改變
                        break;
                    } else { //若是不能成功轉換爲寫鎖
                        sl.unlockRead(stamp);  //咱們顯式釋放讀鎖
                        stamp = sl.writeLock();  //顯式直接進行寫鎖 而後再經過循環再試
                    }
                }
            } finally {
                sl.unlock(stamp); //釋放讀鎖或寫鎖
            }
        }
    }
}

咱們再將前面的裏改爲StampedLock優化

@Slf4j
public class LockExample5 {

    // 請求總數
    public static int clientTotal = 5000;

    // 同時併發執行的線程數
    public static int threadTotal = 200;

    public static int count = 0;

    private final static StampedLock lock = new StampedLock();

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    private static void add() {
        long stamp = lock.writeLock();
        try {
            count++;
        } finally {
            lock.unlock(stamp);
        }
    }
}

這裏和以前的不同的地方就是ui

long stamp = lock.writeLock();
        try {
            count++;
        } finally {
            lock.unlock(stamp);
        }

在加鎖後會返回一個值,解鎖的時候須要傳入這個值。操作系統

相關文章
相關標籤/搜索