高併發中StampedLock理解與應用

ReadWriteLock大家都用過或者聽說過,可以解決多線程同時讀,但只有一個線程能寫的問題。如果我們深入分析ReadWriteLock,會發現它有個潛在的問題:如果有線程正在讀,寫線程需要等待讀線程釋放鎖後才能獲取寫鎖,即讀的過程中不允許寫,這是一種悲觀的讀鎖。
要進一步提升併發的效率,Java 8引入了新的讀寫鎖:StampedLock

一.StampedLock 簡介

基於功能的鎖,具有三種模式來控制讀/寫訪問。StampedLock的狀態由版本和模式組成。鎖獲取方法返回一個表示並控制相對於鎖狀態的訪問的戳記;這些方法的“嘗試”版本可以改爲返回特殊值零,以表示獲取訪問失敗。鎖釋放和轉換方法需要使用圖章作爲參數,如果它們與鎖的狀態不匹配,則會失敗。三種模式是:

  • 寫作:方法writeLock()可能會阻止等待獨佔訪問,並返回一個可用於方法unlockWrite(long)中釋放鎖的標記。tryWriteLock還提供了未定時和定時版本。當鎖保持在寫模式時,可能不會獲得任何讀鎖,並且所有樂觀讀驗證都將失敗。
  • 悲觀讀鎖:方法readLock()可能會阻止等待非排他性訪問,並返回可以在方法中使用unlockRead(long)以釋放鎖的標記。tryReadLock還提供了未定時和定時版本。
  • 樂觀的讀:tryOptimisticRead() 僅當鎖當前未處於寫入模式時,該方法才返回非零戳。方法validate(long)如果自獲取給定標記以來未在寫模式下獲取鎖,則返回true。可以將這種模式視爲讀取鎖的極弱版本,編寫者可以隨時將其破壞。在短的只讀代碼段中使用樂觀模式通常可以減少爭用並提高吞吐量。然而,其使用固有地易碎。樂觀的讀取部分應僅讀取字段並將其保存在局部變量中,以供驗證後使用。在樂觀模式下讀取的字段可能完全不一致,因此用法僅在您足夠熟悉數據表示以檢查一致性和/或重複調用方法時適用validate()。例如,當首先讀取對象或數組引用,然後訪問其字段,元素或方法之一時,通常需要執行這些步驟。

此類還支持有條件地在三種模式之間提供轉換的方法。例如,方法tryConvertToWriteLock(long)嘗試“升級”模式,如果(1)已經處於寫入模式(2)處於讀取模式並且沒有其他讀取器,或者(3)處於樂觀模式並且鎖可用,則返回有效的寫入戳記。這些方法的形式旨在幫助減少某些基於重試的設計中發生的代碼膨脹。

二.區別

ReadWriteLock 支持兩種模式:一種是讀鎖,一種是寫鎖。
StampedLock 支持三種模式,分別是:寫鎖、悲觀讀鎖和樂觀讀。其中,寫鎖、悲觀讀鎖的語義和 ReadWriteLock 的寫鎖、讀鎖的語義非常類似,允許多個線程同時獲取悲觀讀鎖,但是隻允許一個線程獲取寫鎖,寫鎖和悲觀讀鎖是互斥的。不同的是:StampedLock 裏的寫鎖和悲觀讀鎖加鎖成功之後,都會返回一個 stamp;然後解鎖的時候,需要傳入這個 stamp。
StampedLock提供了樂觀讀鎖,可取代ReadWriteLock以進一步提升併發性能;StampedLock是不可重入鎖,ReadWriteLock可重入。

三.常用Api

在這裏插入圖片描述
在這裏插入圖片描述

四.代碼示例

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

    /**
     * 獲取寫鎖
     * @param deltaX
     * @param deltaY
     */
    void move(double deltaX, double deltaY) {
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }

    /**
     *  獲取讀鎖
     * @return
     */
    double distanceFromOrigin() { 
        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);
    }

    /**
     * 獲取樂觀讀,進行鎖的升級
     * @param newX
     * @param newY
     */
    void moveIfAtOrigin(double newX, double newY) { // 
        // 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);
        }
    }
}

本文的分享暫時就到這裏,希望對您有所幫助
關注 Java有貨領取更多資料

聯繫小編。微信:372787553,帶您進羣互相學習
左側小編微信,右側獲取免費資料
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章