Java8引入的一種新的鎖機制——StampedLock

StampedLock

StampedLock是Java8引入的一種新的鎖機制,簡單的理解,可以認爲它是讀寫 鎖的一個改進版本,讀寫鎖雖然分離了讀和寫的功能,使得讀與讀之間可以完全並 發,但是讀和寫之間依然是衝突的,讀鎖會完全阻塞寫鎖,它使用的依然是悲觀的 鎖策略.如果有大量的讀線程,他也有可能引起寫線程的飢餓。

而 StampedLock 則提供了一種樂觀的讀策略,這種樂觀策略的鎖非常類似於 無鎖的操作,使得樂觀鎖完全不會阻塞寫線程。

它的思想是讀寫鎖中讀不僅不阻塞讀,同時也不應該阻塞寫。

讀不阻塞寫的實現思路

在讀的時候如果發生了寫,則應當重讀而不是在讀的時候直接阻塞寫!即讀 寫之間不會阻塞對方,但是寫和寫之間還是阻塞的!

StampedLock 的內部實現是基於 CLH 的。

廢話不多說直接上代碼:

/**
 * 類說明:JDK1.8源碼自帶的示例
 */
public class StampedLockDemo {
    //一個點的x,y座標
    private double x,y;
    /**Stamped類似一個時間戳的作用,每次寫的時候對其+1來改變被操作對象的Stamped值
     * 這樣其它線程讀的時候發現目標對象的Stamped改變,則執行重讀*/
    private final StampedLock sl = new StampedLock();

    //【寫鎖(排它鎖)】
    void move(double deltaX,double deltaY) {// an exclusively locked method
        /**stampedLock調用writeLock和unlockWrite時候都會導致stampedLock的stamp值的變化
         * 即每次+1,直到加到最大值,然後從0重新開始*/
        long stamp =sl.writeLock(); //寫鎖
        try {
            x +=deltaX;
            y +=deltaY;
        } finally {
            sl.unlockWrite(stamp);//釋放寫鎖
        }
    }

    //【樂觀讀鎖】
    double distanceFromOrigin() { // A read-only method
        /**
         * tryOptimisticRead是一個樂觀的讀,使用這種鎖的讀不阻塞寫
         * 每次讀的時候得到一個當前的stamp值(類似時間戳的作用)
         */
        long stamp = sl.tryOptimisticRead();
        //這裏就是讀操作,讀取x和y,因爲讀取x時,y可能被寫了新的值,所以下面需要判斷
        double currentX = x, currentY = y;
        /**如果讀取的時候發生了寫,則stampedLock的stamp屬性值會變化,此時需要重讀,
         * validate():比較當前stamp和獲取樂觀鎖得到的stamp比較,不一致則失敗。
         * 再重讀的時候需要加讀鎖(並且重讀時使用的應當是悲觀的讀鎖,即阻塞寫的讀鎖)
         * 當然重讀的時候還可以使用tryOptimisticRead,此時需要結合循環了,即類似CAS方式
         * 讀鎖又重新返回一個stampe值*/
        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
        // 讀鎖(這裏可用樂觀鎖替代)
        long stamp = sl.readLock();
        try {
            //循環,檢查當前狀態是否符合
            while (x == 0.0 && y == 0.0) {
                long ws = sl.tryConvertToWriteLock(stamp);
                //如果寫鎖成功
                if (ws != 0L) {
                    stamp = ws;// 替換stamp爲寫鎖戳
                    x = newX;//修改數據
                    y = newY;
                    break;
                }
                //轉換爲寫鎖失敗
                else {
                    //釋放讀鎖
                    sl.unlockRead(stamp);
                    //獲取寫鎖(必要情況下阻塞一直到獲取寫鎖成功)
                    stamp = sl.writeLock();
                }
            }
        } finally {
            //釋放鎖(可能是讀/寫鎖)
            sl.unlock(stamp);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章