摘要:測試吞吐量的時候,是通過每一種實現(xiàn)都重復(fù)測試超過次,每一次都運行秒以上,以保證系統(tǒng)足夠預(yù)熱,下面的結(jié)果都是第次之后平均每秒吞吐量。以我的經(jīng)驗看,教學和開發(fā)中的無鎖算法,不僅能顯著改善吞吐量同時他們也提供更低的延遲。
上周在由Heinz Kabutz通過JCrete?組織的開放空間會議(unconference)上,我參加一個新的java規(guī)范 JSR166?StampedLock 的審查會議。 StampedLock 是為了解決多個readers?并發(fā)訪問共享狀態(tài)時,系統(tǒng)出現(xiàn)的內(nèi)存地址競爭問題。在設(shè)計上通過使用樂觀的讀操作, StampedLock
比 ReentrantReadWriteLock 更加高效;
在會議期間,我突然意思到兩點:
我想是時候該去回顧java中鎖的實現(xiàn)的現(xiàn)狀。
雖然StampedLock 看上去是JDK很好的補充,但是似乎忽略了一個事實,即在多個reader的場景里,無鎖的算法通常是更好的解決方案。
測試為了比較不同的實現(xiàn)方式,我需要采用一種不偏向任意一方的API測試用例。 比如:API必須不產(chǎn)生垃圾、并且允許方法是原子性的。一個簡單的測試用例是設(shè)計一個可在兩維空間中移動其位置的太空船,它位置的坐標可以原子性的讀??;每一次事物里至少需要讀寫2個域,這使得并發(fā)變得非常有趣;
/** * 并發(fā)接口,表示太空船可以在2維的空間中移動位置;并且同時更新讀取位置 */ public interface Spaceship { /** * 讀取太空船的位置到參數(shù)數(shù)組 coordinates 中 * * @param coordinates 保存讀取到的XY坐標. * @return 當前的狀態(tài) */ 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通過分解一個不變的位置對象,本身是干凈的。但是我想保證它不產(chǎn)生垃圾,并且需要能最直接的更新多個內(nèi)容域。這個API可以很容易地擴展到三維空間,并實現(xiàn)原子性要求。
為每一個飛船都設(shè)置多個實現(xiàn),并且作為一個測試套件。本文中所有的代碼和結(jié)果都可以在這里找到。
該測試套件會依次運行每一種實現(xiàn).并且使用?megamorphic dispatch模式,防止并發(fā)訪問中的方法內(nèi)聯(lián)(inlining),鎖粗化(lock-coarsening),循環(huán)展開(loop unrolling)的問題;
每種實現(xiàn)都執(zhí)行下面4個不同的線程的情況,結(jié)果也是不同的;
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的環(huán)境上。
測試吞吐量的時候,是通過每一種實現(xiàn)都重復(fù)測試超過5次,每一次都運行5秒以上,以保證系統(tǒng)足夠預(yù)熱,下面的結(jié)果都是第5次之后平均每秒吞吐量。為了更像一個典型的java應(yīng)用;沒有采用會導致明顯減少差異的線程依附性(thread affinity)和多核隔離(core isolation?)技術(shù);
結(jié)果上述圖表的原始數(shù)據(jù)可以在這里找到
分析結(jié)果里面真正令我吃驚的是ReentrantReadWriteLock的性能,我沒有想到的是,在這樣的場景下它在讀和少量寫之間取得的巨大的平衡性,
我主要的收獲:
StampedLock 對現(xiàn)存的鎖實現(xiàn)有巨大的改進,特別是在讀線程越來越多的場景下:
StampedLock有一個復(fù)雜的API,對于加鎖操作,很容易誤用其他方法;
當只有2個競爭者的時候,Synchronised是一個很好的通用的鎖實現(xiàn);
當線程增長能夠預(yù)估,ReentrantLock是一個很好的通用的鎖實現(xiàn);
選擇使用ReentrantReadWriteLock時,必須經(jīng)過小心的適度的測試;所有重大的決定,必須在基于測試數(shù)據(jù)的基礎(chǔ)上做決定;
無鎖的實現(xiàn)比基于鎖的算法有更好短吞吐量;
結(jié)論:非常開心能看到無鎖技術(shù)對基于鎖的算法的影響; 樂觀鎖的策略,實際上就是一個無鎖算法技術(shù)。
以我的經(jīng)驗看,教學和開發(fā)中的無鎖算法,不僅能顯著改善吞吐量;同時他們也提供更低的延遲。
原文 Lock based vs lock free concurrent
翻譯 曹姚君?校對 方騰飛
via ifeve
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/64054.html
摘要:在本例中,講述的無鎖來自于并發(fā)包我們將這個無鎖的稱為。在這里,我們使用二維數(shù)組來表示的內(nèi)部存儲,如下變量存放所有的內(nèi)部元素。為什么使用二維數(shù)組去實現(xiàn)一個一維的呢這是為了將來進行動態(tài)擴展時可以更加方便。 我們已經(jīng)比較完整得介紹了有關(guān)無鎖的概念和使用方法。相對于有鎖的方法,使用無鎖的方式編程更加考驗一個程序員的耐心和智力。但是,無鎖帶來的好處也是顯而易見的,第一,在高并發(fā)的情況下,它比有鎖...
摘要:因為多線程競爭鎖時會引起上下文切換。減少線程的使用。舉個例子如果說服務(wù)器的帶寬只有,某個資源的下載速度是,系統(tǒng)啟動個線程下載該資源并不會導致下載速度編程,所以在并發(fā)編程時,需要考慮這些資源的限制。 最近私下做一項目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Jav...
摘要:相比與其他操作系統(tǒng)包括其他類系統(tǒng)有很多的優(yōu)點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。因為多線程競爭鎖時會引起上下文切換。減少線程的使用。很多編程語言中都有協(xié)程。所以如何避免死鎖的產(chǎn)生,在我們使用并發(fā)編程時至關(guān)重要。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關(guān)鍵字(1) java多線程學習(二)syn...
摘要:本文旨在對鎖相關(guān)源碼本文中的源碼來自使用場景進行舉例,為讀者介紹主流鎖的知識點,以及不同的鎖的適用場景。中,關(guān)鍵字和的實現(xiàn)類都是悲觀鎖。自適應(yīng)意味著自旋的時間次數(shù)不再固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。 前言 Java提供了種類豐富的鎖,每種鎖因其特性的不同,在適當?shù)膱鼍跋履軌蛘宫F(xiàn)出非常高的效率。本文旨在對鎖相關(guān)源碼(本文中的源碼來自JDK 8)、使用場景...
閱讀 1506·2021-11-22 09:34
閱讀 1454·2021-09-22 14:57
閱讀 3508·2021-09-10 10:50
閱讀 1547·2019-08-30 15:54
閱讀 3747·2019-08-29 17:02
閱讀 3530·2019-08-29 12:54
閱讀 2684·2019-08-27 10:57
閱讀 3379·2019-08-26 12:24