亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

讀寫鎖 StampedLock

1. 前言

本節帶領大家認識第二個常用的 Java 并發鎖工具之 StampedLock。

本節先簡單介紹 StampedLock 的基本概念,然后介紹關鍵的編程方法,最后通過一個編程例子為大家展示 StampedLock 工具類的用法。

下面我們正式開始介紹吧。

2. 概念解釋

我們先解釋一組概念:悲觀鎖、樂觀鎖。

悲觀鎖 指的是對數據修改持保守態度,在整個數據處理時,先將數據鎖定狀態,然后再進行修改處理。之所以叫做悲觀鎖,是因為這是一種對數據的修改抱有悲觀態度的并發控制方式,認為數據被并發修改的概率比較大,所以需要在修改之前先加鎖。

樂觀鎖 正好相反,抱著數據訪問一般情況下不會造成沖突的觀點,先對數據做修改,在正式提交修改結果是才會做沖突檢查,如果發現修改的是舊版本的數據,則返回修改失敗,否則提交修改。

StampedLock 是對 ReentrantReadWriteLock 的改進。相比 ReentrantReadWriteLock 采用了悲觀鎖的思想對數據修改的并發控制,StampedLock 使用了樂觀思想的加鎖實現,具有更高的并發性。

下面我們學習其關鍵的編程方法。

3. StampedLock 的編程方法

StampedLock 提供了三種并發控制模式,介紹這三種模式過程中,我們穿插介紹關鍵的編程方法。

3.1. 獨占寫模式

功能和 ReentrantReadWriteLock 的寫鎖類似。獨占寫模式相關的幾個方法如下。

long stamp = writeLock () 方法:獲取獨占寫鎖,可能會被阻塞。如果獲取鎖成功則返回一個 stamp;
tryWriteLock () 方法:嘗試獲取獨占寫鎖,類似 writeLock () 方法,只是獲取不到時立刻返回不會阻塞;
tryWriteLock (long time, TimeUnit unit) 方法:允許在給定的時間內嘗試獲取獨占寫鎖,超時仍然未獲取到時則返回;
writeLockInterruptibly () 方法:類似 writeLock () 但允許獲取鎖的過程被打斷;
unlockWrite (long stamp) 方法:用于釋放獨占寫鎖;
tryUnlockWrite () 方法:類似 unlockWrite (), 但允許不需要 stamp 郵戳參數。

3.2. 悲觀讀模式

功能和 ReentrantReadWriteLock 的讀鎖類似。悲觀讀模式相關的幾個方法如下。

long stamp = readLock () 方法:獲取獨占讀鎖,可能會被阻塞。如果獲取鎖成功則返回一個 stamp;
unlockRead (long stamp) 方法:用于釋放讀鎖;
tryReadLock () 方法:嘗試獲取讀鎖,類似 readLock () 方法,只是獲取不到時立刻返回不會阻塞;
tryReadLock (long time, TimeUnit unit) 方法:允許在給定的時間內嘗試獲取讀鎖,超時仍然未獲取到時則返回;
readLockInterruptibly () 方法:類似 readLock () 但允許獲取鎖的過程被打斷。

3.3. 樂觀讀模式

這是一種優化的讀模式。樂觀讀模式相關的幾個方法如下。

tryOptimisticRead () 方法:非阻塞嘗試樂觀獲取讀鎖,只有當寫鎖沒有被獲取時返回一個非 0 的 stamp 。樂觀讀取模式適用于短時間讀取操作,降低競爭和提高吞吐量。在使用時一般需將數據存儲到一個副本中,在后繼處理中用于對比數據是否是最新狀態;
validate (long stamp) 方法:用于檢查在獲取到讀鎖 stamp 后,鎖有沒被其他寫線程搶占。如果寫鎖沒有被獲取,那么 validate () 方法返回 true??啥啻握{用驗證這一信息。

另外,此類也提供了一組讀寫鎖之間的轉換方法

tryConvertToWriteLock (long stamp) 方法:嘗試轉換為寫鎖。轉換條件:
tryConvertToReadLock (long stamp) 方法:嘗試轉換為悲觀讀鎖。
tryConvertToOptimisticRead (long stamp) 方法:嘗試轉換為樂觀讀鎖。

注意此類的編程方法有這樣一個共通特征:
所有獲取鎖的方法,都返回一個郵戳(Stamp),Stamp 為 0 表示獲取失敗,其余都表示成功;
所有釋放鎖的方法,都需要一個郵戳(Stamp),這個 Stamp 必須是和成功獲取鎖時得到的 Stamp 一致;

下面我們舉一個具體的編程例子。

4. 編程示例

上面介紹了核心編程方法,我們給出一個非常簡潔明了的官方例子,切實體會一下 StampedLock 的用法。

import java.util.concurrent.locks.StampedLock;

public class StampedLockTest {

    // 成員變量
    private double x, y;

    // 鎖實例
    private final StampedLock sl = new StampedLock();

    // 排它鎖-寫鎖(writeLock)
    void move(double deltaX, double deltaY) {
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }

    // 一個只讀方法
    // 其中存在樂觀讀鎖到悲觀讀鎖的轉換
    double distanceFromOrigin() {

        // 嘗試獲取樂觀讀鎖
        long stamp = sl.tryOptimisticRead();
        // 將全部變量拷貝到方法體棧內
        double currentX = x, currentY = y;
        // 檢查在獲取到讀鎖stamp后,鎖有沒被其他寫線程搶占
        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) {
        long stamp = sl.tryOptimisticRead();
        try {
            // 如果當前點在原點則移動
            while (x == 0.0 && y == 0.0) {
                // 嘗試將獲取的讀鎖升級為寫鎖
                long ws = sl.tryConvertToWriteLock(stamp);
                // 升級成功,則更新stamp,并設置坐標值,然后退出循環
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    break;
                } else {
                    // 讀鎖升級寫鎖失敗則釋放讀鎖,顯示獲取獨占寫鎖,然后循環重試
                    sl.unlockRead(stamp);
                    stamp = sl.writeLock();
                }
            }
        } finally {
            sl.unlock(stamp);
        }
    }
}

注意在使用時,獲取鎖的操作應該放在 try 之前,而釋放鎖的操作需要放在 finally 中,可確保鎖釋放。另外需要注意 StampedLock 具有不可重入性。

5. 小結

本節解釋了 StampedLock 的基本概念和主要的編程方法,且通過一個簡單的例子展示了其用法,更多關于此工具類的概念和原理介紹,可閱讀 “Java 并發原理入門教程” 。希望大家在學習過程中,多思考勤練習,早日掌握之。