天天看點

基于鎖的并發算法 vs 無鎖的并發算法

上周在由 Heinz Kabutz 通過 JCrete  組織的開放空間會議(unconference)上,我參加一個新的java規範 JSR166 StampedLock

的審查會議。 StampedLock 是為了解決多個readers 并發通路共享狀态時,系統出現的記憶體位址競争問題。在設計上通過使用樂觀的讀操作, StampedLock

ReentrantReadWriteLock

更加高效;

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

  1. 我想是時候該去回顧java中鎖的實作的現狀。
  2. 雖然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);

 }

上面的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 )技術;

結果

基于鎖的并發算法 vs 無鎖的并發算法
基于鎖的并發算法 vs 無鎖的并發算法
基于鎖的并發算法 vs 無鎖的并發算法
基于鎖的并發算法 vs 無鎖的并發算法

上述圖表的原始資料可以在

找到

分析

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

我主要的收獲:

  1. StampedLock 對現存的鎖實作有巨大的改進,特别是在讀線程越來越多的場景下:
  2. StampedLock有一個複雜的API,對于加鎖操作,很容易誤用其他方法;
  3. 當隻有2個競争者的時候,Synchronised是一個很好的通用的鎖實作;
  4. 當線程增長能夠預估,ReentrantLock是一個很好的通用的鎖實作;
  5. 選擇使用ReentrantReadWriteLock時,必須經過小心的适度的測試;所有重大的決定,必須在基于測試資料的基礎上做決定;
  6. 無鎖的實作比基于鎖的算法有更好短吞吐量;

結論:

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

以我的經驗看,

教學

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