StampedLock的理解和使用

StampedLock介紹

StampedLock是爲了優化可重入讀寫鎖性能的一個鎖實現工具,jdk8開始引入編程

相比於普通的ReentranReadWriteLock主要多了一種樂觀讀的功能工具

在API上增長了stamp的入參和返回值性能

不支持重入測試

StampedLock如何使用和使用價值

我看了上面的介紹仍然對StampedLock一頭霧水,下面咱們來揭開StampedLock神祕的面紗優化

一、對於悲觀讀和悲觀寫的方法與ReentranReadWriteLock讀寫鎖效果同樣

下面是StampedLock的悲觀讀、寫鎖的實現spa

static ExecutorService service = Executors.newFixedThreadPool(10);
static StampedLock lock = new StampedLock();
static long milli = 5000;
static int count = 0;

private static long writeLock() {
  long stamp = lock.writeLock(); //獲取排他寫鎖
  count+=1;
  lock.unlockWrite(stamp); //釋放寫鎖
  System.out.println("數據寫入完成");
  return System.currentTimeMillis();
}線程

    private static void readLock() {//悲觀讀鎖
        service.submit(() -> {
            int currentCount = 0;
            long stamp = lock.readLock(); //獲取悲觀讀鎖
            try {
                currentCount = count; //獲取變量值
                try {
                    TimeUnit.MILLISECONDS.sleep(milli);//模擬讀取須要花費20秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                lock.unlockRead(stamp); //釋放讀鎖
            }
            System.out.println("readLock==" + currentCount); //顯示最新的變量值
        });
        try {
            TimeUnit.MILLISECONDS.sleep(500);//要等一等讀鎖先得到
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

測試一下效果:code

public static void main(String[] args) {
  long l1 = System.currentTimeMillis();
  readLock();  long l2 = writeLock();
  System.out.println(l2-l1);
}

由於我對悲觀讀操做進行了5秒的數據讀取延遲,因此寫操做要等5秒後讀鎖釋放才能寫入數據blog

輸出結果:it

數據寫入完成的時間比獲取讀鎖晚5043ms

讀到的數據仍然是寫入前的0

二、對於樂觀讀(若是沒有進入寫模式)能夠減小一次讀鎖的性能消耗,而且不會阻塞寫入的操做

咱們添加了一個樂觀讀的方法,這個方法仍然模擬讀取延遲5秒,樂觀讀實現須要參考下面的編程模式(獲取樂觀鎖、校驗是否進入寫模式)

    private static void optimisticRead() {
        service.submit(() -> {
            long stamp = lock.tryOptimisticRead(); //嘗試獲取樂觀讀鎖
            int currentCount = count; //獲取變量值
            if (!lock.validate(stamp)) { //判斷count是否進入寫模式
                stamp = lock.readLock(); //已經進入寫模式,沒辦法只能老老實實的獲取讀鎖
                try {
                    currentCount = count; //成功獲取到讀鎖,並從新獲取最新的變量值
                } finally {
                    lock.unlockRead(stamp); //釋放讀鎖
                }
            }
            try {
                TimeUnit.MILLISECONDS.sleep(milli);//模擬讀取須要花費20秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //走到這裏,說明count尚未被寫,那麼能夠不用加讀鎖,減小了讀鎖的開銷
            System.out.println("optimisticRead==" + currentCount); //顯示最新的變量值
        });
        try {
            TimeUnit.MILLISECONDS.sleep(500);//要等一等讀鎖先得到
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

測試一下效果:

public static void main(String[] args) {
  long l1 = System.currentTimeMillis();
  optimisticRead();
  long l2 = writeLock();
  System.out.println(l2-l1);
}

直接看一下輸出結果:

數據寫入完成的時間比獲取讀鎖晚543ms(說明樂觀讀並無阻塞寫操做)

5秒後讀到的數據仍然是寫入前的0

總結

能夠看到相比直接用悲觀讀鎖,樂觀讀鎖能夠:

一、進入悲觀讀鎖前先看下有沒有進入寫模式(說白了就是有沒有已經獲取了悲觀寫鎖)

二、若是其餘線程已經獲取了悲觀寫鎖,那麼就只能老老實實的獲取悲觀讀鎖(這種狀況至關於退化成了讀寫鎖)

三、若是其餘線程沒有獲取悲觀寫鎖,那麼就不用獲取悲觀讀鎖了,減小了一次獲取悲觀讀鎖的消耗和避免了由於讀鎖致使寫鎖阻塞的問題,直接返回讀的數據便可(必須再tryOptimisticRead和validate之間獲取好數據,不然數據可能會不一致了,試想若是過了validate再獲取數據,這時數據可能被修改而且讀操做也沒有任何保護措施)

相關文章
相關標籤/搜索