1. 程式人生 > >基於鎖的併發演算法 vs 無鎖的併發演算法

基於鎖的併發演算法 vs 無鎖的併發演算法

上週在由Heinz Kabutz通過JCrete 組織的開放空間會議(unconference)上,我參加一個新的java規範 JSR166 StampedLock的審查會議。StampedLock 是為了解決多個readers 併發訪問共享狀態時,系統出現的記憶體地址競爭問題。在設計上通過使用樂觀的讀操作,StampedLock 比ReentrantReadWriteLock 更加高效;

在會議期間,我突然意思到兩點:

  • 第一、我想是時候該去回顧java中鎖的實現的現狀。
  • 第二、雖然StampedLock 看上去是JDK很好的補充,但是它視乎忽略了一個事實,即在多個reader的場景裡,無鎖的演算法通常是更好的解決方案。

 測試:

為了比較不同的實現方式,我需要採用一種不偏向任意一方的API測試用例。 比如:API必須不產生垃圾、並且允許方法是原子性的。一個簡單的測試用例是設計一個可在兩維空間中移動其位置的太空船,它位置的座標可以原子性的讀取;每一次事物裡至少需要讀寫2個域,這使得併發變得非常有趣;

/**
 * 併發介面,表示太空船可以在2維的空間中移動位置;並且同時更新讀取位置
 */
 public interface Spaceship
 {
 /**
 * 讀取太空船的位置到引數陣列 coordinates 中
 *
 * @param coordinates 儲存讀取到的XY座標.
 * @return 當前的狀態
 */
 int readPosition(final int[] coordinates);

/**
 * 通過增加XY的值表示移動太空船的位置。
 *
 * @param xDelta x座標軸上移動的增量.
 * @param yDelta y座標軸上移動的增量.
 * @return the number of attempts made to write the new coordinates.
 */
 int move(final int xDelta, final int yDelta);
 }
<pre>

上面的API通過分解一個不變的位置物件,本身是乾淨的 。但是我想保證它不產生垃圾,並且需要能最直接的更新多個內容域。這個API可以很容易地擴充套件到三維空間,並實現原子性要求。

為每一個飛船都設定多個實現,並且作為一個測試套件。本文中所有的程式碼和結果都可以在這裡找到。

測試套件會依次執行每一種實現.並且使用 megamorphic dispatch模式,防止併發訪問中的方法內聯(inlining),鎖粗化(lock-coarsening),迴圈展開( loop unrolling)的問題;

每種實現都執行下面4個不同的執行緒的情況,結果也是不同的;

1 reader – 1 writer

2 readers – 1 writer

3 readers – 1 writer

2 readers – 2 writers

所有的測試執行在64位機器、Java版本:1.7.0_25、 Linux版本:3.6.30、4核 2.2GHz Ivy Bridge (第三代Core i系列處理器)i7-3632QM的環境上。

測試吞吐量的時候,是通過每一種實現都重複測試超過5次,每一次都執行5秒以上,以保證系統足夠預熱,下面的結果都是第5次之後平均每秒吞吐量。為了更像一個典型的java應用;沒有采用會導致明顯減少差異的執行緒依附性(thread affinity)和多核隔離(core isolation )技術;

結果:


上述圖表的原始資料可以在這裡找到

分析:

結果裡面真正令我吃驚的是ReentrantReadWriteLock的效能,我沒有想到的是,在這樣的場景下它在讀和少量寫之間取得的巨大的平衡性,

我主要的收穫:

1、StampedLock 對現存的鎖實現有巨大的改進,特別是在讀執行緒越來越多的場景下:

2、StampedLock有一個複雜的API,對於加鎖操作,很容易誤用其他方法;

3、當只有2個競爭者的時候,Synchronised是一個很好的通用的鎖實現;

4、當執行緒增長能夠預估,ReentrantLock是一個很好的通用的鎖實現;

5、選擇使用ReentrantReadWriteLock時,必須經過小心的適度的測試 ;所有重大的決定,必須在基於測試資料的基礎上做決定;

6、無鎖的實現比基於鎖的演算法有更好短吞吐量;

結論:

非常開心能看到無鎖技術對基於鎖的演算法的影響; 樂觀鎖的策略,實際上就是一個無鎖演算法技術。

以我的經驗看,教學和開發中的無鎖演算法,不僅能顯著改善吞吐量;同時他們也提供更低的延遲。