讀寫鎖效能之王 StampedLock

2022-05-12 17:33:41 字數 2543 閱讀 5272

stampedlock是jdk1.8中新增的乙個讀寫鎖,也是對jdk1.5中的讀寫鎖reentrantreadwritelock的優化。

主要包括讀寫鎖之間的轉換及更加細粒度併發控制等,前者提供的是不可重入鎖,後者的是可重入鎖,但是前者通過了樂觀讀鎖在多執行緒併發中的讀多情況下有更好的效能,因為stampedlock獲取樂觀讀鎖時,居然不需要通過cas操作來設定鎖的狀態,只是簡單地通過測試狀態即可。

當呼叫獲取鎖之類的方法後,會返回乙個long型變數:stamp(戳記,long型別),代表了鎖的狀態。當stamp返回0時,表示執行緒獲取鎖失敗。並且,當釋放鎖或者轉換鎖的時候,都要傳入最初獲取的stamp值。

stampedlock提供三種模式的鎖,如下。

stampedlock還支援這三種鎖在一定條件下進行相互轉換。例如tryconverttowritelock(long)期望把stamp標識的鎖公升級為寫鎖,這個方法會在以下幾種情況下返回乙個有效的stamp:

teststampedlock類裡有兩個成員變數x,y,組成乙個座標,例項化乙個stampedlock物件來保證操作的原子性。

public class teststampedlock
move方法:改變x,y的值。首先獲取了寫鎖,然後修改x,y的值,最後釋放寫鎖。由於stampedlock的寫鎖是獨佔鎖,當其他執行緒呼叫move方法時,會被阻塞。也保證了其他執行緒不能獲取讀鎖來讀取x,y的值,保證了對x,y操作的原子性和資料的一致性。

/**

* 獨佔鎖

** @param deltax

* @param deltay

*/public void move(double deltax, double deltay) finally

}

distancefromorigin方法:計算當前座標到原點的距離。(1)獲取了樂觀讀鎖,如果當前沒有其他執行緒獲取到了寫鎖,那麼(1)的返回值就是非0,(2)複製了座標變數到本地方法棧中。

(3)檢查(1)獲取到的stamp值是否還有效。之所以要檢測,是因為(1)獲取樂觀讀鎖的時候沒有通過cas修改狀態,而是通過為運算子返回乙個stamp,在這裡校驗是看在獲取stamp後判斷前是否有其他執行緒持有寫鎖,如果有的話,則stamp無效。

(7)在計算期間,也有可能其他執行緒在這段時間裡獲取了寫鎖,並修改了x,y值,而(7)操作的是方法棧裡的值,也就是快照而已,並不是最新的值。

(3)校驗失敗後,會獲取悲觀讀鎖,這時候如果有其他執行緒持有了寫鎖,則(4)會一直阻塞至其他執行緒釋放了寫鎖,否則,當前執行緒獲取到了讀鎖,執行(5)(6)。**在執行(5)的時候,由於加了讀鎖,所以在這期間其他執行緒獲取寫鎖的時候會阻塞,這保證了資料的一致性。

另外,這裡的x,y沒有被宣告volatile會不會記憶體不可見,答案是不會的,因為加鎖的語義儲存了記憶體可見性。

當然,最後計算的值,依然有可能不是最新的。

/**

* 樂觀鎖

*/public double distancefromorigin() finally

}// (7)返回計算結果

return math.sqrt(currentx * currentx + currenty * currenty);

}

moveatorigin方法:如果當前座標在原點,則移動座標。(1)獲取悲觀讀鎖,保證其他執行緒不能獲取寫鎖來修改x,y的值。(2)判斷是否在原點,是的話,則(3)嘗試公升級讀鎖為寫鎖,因為這時候可能有多個執行緒持有該悲觀讀鎖,所以不一定能公升級成功。當多個執行緒都執行到(3)時,則只有乙個可以公升級成功,然後執行(4)更新stamp,修改座標值,退出迴圈。失敗的話,執行(5),先釋放讀鎖,再申請寫鎖,再迴圈。最後執行(6)釋放鎖。

/**

* 使用悲觀鎖獲取讀鎖,並嘗試轉換為寫鎖

*/public void moveatorigin(double newx, double newy) else

}} finally

}

這裡在使用樂觀鎖的時候,要考慮得比較多,必須要保證以下順序:

// 非阻塞獲取樂觀鎖

long stamp = lock.tryoptimisticread();

// 複製變數到本地方法棧中

copy();

// 校驗stamp是否生效

if (!validate(stamp)) finally

}

所有獲取鎖的方法,都會返回乙個stamp戳記,stamp為0表示獲取失敗,其餘均表示成功。

所有釋放鎖的方法,都需要乙個stamp戳記,這個stamp必須和成功獲取鎖時返回的stamp一樣。

stampedlock是不可重入的鎖,並且讀效能比reentrantreadwritelock好,

stampedlock支援讀鎖和寫鎖的互相轉換。

reentrantreadwritelock的鎖被占用的時候,如果其他執行緒嘗試獲取寫鎖的時候,會被阻塞,但是,stampedlock在樂觀獲取鎖後,其他執行緒嘗試獲取寫鎖,也不會被阻塞,這其實是對讀鎖的優化,所以,在獲取樂觀讀鎖後,還需要對結果進行校驗。

Java8對讀寫鎖的改進 StampedLock

該類是乙個讀寫鎖的改進,它的思想是讀寫鎖中讀不僅不阻塞讀,同時也不應該阻塞寫。讀不阻塞寫的實現思路 在讀的時候如果發生了寫,則應當重讀而不是在讀的時候直接阻塞寫!因為在讀執行緒非常多而寫執行緒比較少的情況下,寫執行緒可能發生飢餓現象,也就是因為大量的讀執行緒存在並且讀執行緒都阻塞寫執行緒,因此寫執行...

讀寫鎖與自旋鎖

一 讀寫鎖 1 特點 讀寫鎖比mutex有更高的適用性,可以多個執行緒同時占用讀模式的讀寫鎖,但是只能乙個執行緒占用寫模式的讀寫鎖。1 當讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的執行緒都會被阻塞 2 當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的執行緒都可以得到訪問權...

互斥鎖和讀寫鎖

互斥鎖的型別 對資源的訪問是互斥的,即執行緒a對資源加鎖後,在a解鎖前,其他執行緒不能訪問這個加鎖的資源。互斥鎖的特點 多個執行緒訪問資源的時候是序列的 互斥鎖的使用步驟 建立乙個互斥鎖 pthread mutex t mutex 初始化這把鎖 pthread mutex init mutex,nu...