JDK8对并发的新支持
2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1.?LongAdder
AtomicLong的實現(xiàn)方式是內(nèi)部有個value變量,當(dāng)多線程并發(fā)自增,自減時,均通過CAS指令從機(jī)器指令級別保證并發(fā)的原子性。唯一會制約AtomicLong高效的原因是高并發(fā),高并發(fā)意味著CAS的失敗幾率更高,重試次數(shù)更多,越多線程重試,CAS失敗幾率又越高,變成惡性循環(huán),AtomicLong效率降低。
而LongAdder將把一個value拆分成若干個cell,把所有cell加起來,就是value。所以對LongAdder進(jìn)行加減操作,只需要對不同的cell來操作,不同的線程對不同的cell進(jìn)行CAS操作,CAS的成功路當(dāng)然高了(試想一下3+2+1=6,一下線程3+1,另一個線程2+1,最后是8,LongAdder沒有乘法除法的API)。
可是在并發(fā)數(shù)不是很高的情況,拆分成若干個的cell,還需要維護(hù)cell和求和,效率不如AtomicLong的實現(xiàn)。LongAdder用了巧妙的辦法來解決了這個問題。
初始情況,LongAdder與AtomicLong是相同的,只有在CAS失敗時,才會將value拆分成cell,每失敗一次,都會增加cell的數(shù)量,這樣在低并發(fā)時,同樣高效,在高并發(fā)時,這種"自適應(yīng)"的處理方式,達(dá)到一定cell數(shù)量后,CAS將不會失敗,效率大大提高。
LongAdder是一種以空間換時間的策略。
2. CompletableFuture
- 實現(xiàn)CompletionStage接口(40余個方法),大多數(shù)方法多數(shù)應(yīng)用在函數(shù)式編程中
- Java 8中對Future的增強(qiáng)版
- 支持流式調(diào)用
簡單的實現(xiàn):
| import java.util.concurrent.CompletableFuture; public class AskThread implements Runnable { ? ? public AskThread(CompletableFuture<Integer> re) { ? ? @Override ? ? public static void main(String[] args) throws InterruptedException { |
Future最令人詬病的就是要等待,要自己去檢查任務(wù)十分完成了,在Future中,任務(wù)完成的時間是不可控的。而CompletableFuture的最大改進(jìn)在于,任務(wù)完成的時間也開放了出來:future.complete(60); 用來設(shè)置完成時間。
CompletableFuture的異步執(zhí)行:
| ?? ?public static Integer calc(Integer para) { ? ? public static void main(String[] args) throws InterruptedException, |
CompletableFuture的流式調(diào)用:
| ?? ?public static Integer calc(Integer para) { ? ? public static void main(String[] args) throws InterruptedException, |
組合多個CompletableFuture:
| ?? ?public static Integer calc(Integer para) { ? ? public static void main(String[] args) throws InterruptedException, |
這幾個例子更多是側(cè)重Java8的一些新特性,這里就簡單舉個例子來說明特性,就不深究了。CompletableFuture跟性能上關(guān)系不大,更多的是為了支持函數(shù)式編程,在功能上的增強(qiáng)。當(dāng)然開放了完成時間的設(shè)置是一大亮點。
3. StampedLock
在上一篇中剛剛提出了鎖分離,而鎖分離的重要的實現(xiàn)就是ReadWriteLock。而StampedLock則是ReadWriteLock的一個改進(jìn)。StampedLock與ReadWriteLock的區(qū)別在于,StampedLock認(rèn)為讀不應(yīng)該阻塞寫,StampedLock認(rèn)為當(dāng)讀寫互斥的時候,讀應(yīng)該是重讀,而不是不讓寫線程寫。這樣的設(shè)計解決了讀多寫少時,使用ReadWriteLock會產(chǎn)生寫線程饑餓現(xiàn)象。
所以StampedLock是一種偏向于寫線程的改進(jìn)。
StampedLock示例:
| import java.util.concurrent.locks.StampedLock; public class Point { ? ? void move(double deltaX, double deltaY) { // an exclusively locked method ? ? double distanceFromOrigin() { // A read-only method |
上述代碼模擬了寫線程和讀線程,StampedLock根據(jù)stamp來查看是否互斥,寫一次stamp變增加某個值
| tryOptimisticRead() |
就是剛剛所說的讀寫不互斥的情況,每次讀線程要讀時,會先判斷:
| if (!sl.validate(stamp)) |
validate中會先查看是否有寫線程在寫,然后再判斷輸入的值和當(dāng)前的stamp是否相同,即判斷是否讀線程將讀到最新的數(shù)據(jù)。如果有寫線程在寫,或者stamp數(shù)值不同,則返回失敗。
如果判斷失敗,當(dāng)然可以重復(fù)地嘗試去讀,在示例代碼中,并沒有讓其重復(fù)嘗試讀,而采用的是將樂觀鎖退化成普通的讀鎖去讀,這種情況就是一種悲觀的讀法。
| stamp = sl.readLock(); |
StampedLock的實現(xiàn)思想:
CLH自旋鎖:當(dāng)鎖申請失敗時,不會立即將讀線程掛起,在鎖當(dāng)中會維護(hù)一個等待線程隊列,所有申請鎖,但是沒有成功的線程都記錄在這個隊列中。每一個節(jié)點(一個節(jié)點代表一個線程),保存一個標(biāo)記位(locked),用于判斷當(dāng)前線程是否已經(jīng)釋放鎖。當(dāng)一個線程試圖獲得鎖時,取得當(dāng)前等待隊列的尾部節(jié)點作為其前序節(jié)點。并使用類似如下代碼判斷前序節(jié)點是否已經(jīng)成功釋放鎖
| while (pred.locked) { } |
這個循環(huán)就是不斷等前面那個結(jié)點釋放鎖,這樣的自旋使得當(dāng)前線程不會被操作系統(tǒng)掛起,從而提高了性能。當(dāng)然不會進(jìn)行無休止的自旋,會在若干次自旋后掛起線程。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
轉(zhuǎn)載于:https://my.oschina.net/u/2484728/blog/893583
總結(jié)
以上是生活随笔為你收集整理的JDK8对并发的新支持的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树莓派GPIO资料
- 下一篇: phpFastCache