三个好用的并发工具类
轉載自??三個好用的并發(fā)工具類
以前的文章中,我們介紹了太多的底層原理技術以及新概念,本篇我們輕松點,了解下 Java 并發(fā)包下、基于這些底層原理的三個框架工具類。
它們分別是:
-
信號量 Semaphore
-
倒計時門栓 CountDownLatch
-
屏障 CyclicBarrier
所以,既然是工具類,那么必然是離不開特定的場景的,于是相互之間沒有誰優(yōu)誰劣,只有誰更合適。
?
信號量 Semaphore
Semaphore 適用于什么樣的使用場景呢,我們舉個通俗的例子:
假如現(xiàn)在有一個停車場,里面有只十個停車位,當著十個停車位都被占用了,外面的車就不允許進入了,就必須在外面等著。出來一輛車才允許進去一輛車
這個場景不同于我們一般的并發(fā)場景,一般來說,我們的臨界資源只能允許一個線程進行訪問,其他線程都地等著。
但是,有一種場景是,臨界資源允許多個線程同時訪問,超過限定數(shù)量的外的線程得阻塞等待。
這種情境使用原始的那一套也是能實現(xiàn)的,但那叫「造輪子」,Java 并發(fā)框架下給我們提供了一個工具類,專門適用這種場景。
Semaphore 可以說是為上述這種場景而生的一個工具類,我們寫個 demo 實現(xiàn)上述邏輯:
執(zhí)行程序之后,你會看到:
?
你看,出來一個線程才允許進去一個線程,這就是 Semaphore。
semaphore 的內部原理其實你去看源碼,你會發(fā)現(xiàn)和我們的 ReentrantLock 的實現(xiàn)是極其類似的,包括公平與非公平策略的支持,只不過,AQS 里面的 state 在前者的實現(xiàn)中,一般小于等于一(除非重入鎖),而后者的 state 則小于等于十,記錄的是剩余可用臨界資源數(shù)量。
所以,semaphore 天生就存在一個問題,如果某個線程重入了臨界區(qū),可用臨界資源的數(shù)量是否需要減少?
停車場一共十個停車位,一輛車進去并占有了一個停車位,過了一段時間,這個向管理員報告,我還要占用一個停車位,先不管他占兩個干啥,此時的管理員會同意嗎?
實際上,在 Java 這個管理員看來,已經進入臨界區(qū)的線程是「老爺」,提出的要求都會優(yōu)先滿足,即便他自身占有的資源并沒有釋放。
所以,在 Semaphore 機制里,一個線程進入臨界區(qū)之后占用掉所有的臨界資源都是可能的。
?
倒計時門栓 CountDownLatch
下面我們來看看這個 CountDownLatch,名字聽起來挺高級,究竟提供了怎樣的功能呢?
有這么一個常見的場景,我們一起來看看:
大家日常經常使用的拼多多,一件商品至少需要兩到三人拼團,商家才會發(fā)貨。
這里,我們不去研究它的商業(yè)模式,不管他是怎么實現(xiàn)盈利的,就這么一種場景,如果要用基本的并發(fā) API 來實現(xiàn),你可能會想到:
來一個線程阻塞一次,知道達到指定的數(shù)量后,全部喚醒
對,沒錯,CountDownLatch 內部就是這樣實現(xiàn)的,輪子已經幫你造好了,我們來看看該怎么實現(xiàn)上述的模型案例:
多運行幾次,你會發(fā)現(xiàn)結果不會錯,拼團的人先后順序可能不同,但商家一定是在三個人都準備好了之后才會發(fā)貨。
除此之外,它還有更多的應用,比如百米賽跑,只有當所有運動員都準備好了之后,裁判員才會吹響哨子,等等等等。
實現(xiàn)原理也基本和顯式鎖類似,不同點依然在于對 state 的控制,CountDownLatch 只判斷 state 是否等于零,不等于零就說明時機未到,阻塞當前線程。
而每一次的 countDown 方法調用都會減少一次倒計時資源,直至為零才喚醒阻塞的線程。
?
循環(huán)屏障 CyclicBarrier
CyclicBarrier 其實和 CountDownLatch 很像,我們先介紹完 CyclicBarrier,然后再和你一起去比較比較他倆的區(qū)別和相似點。
考慮這么一個場景:
公寓的班車總是在公寓樓下裝滿一車人之后,出發(fā)并開到地鐵站,接著再回來接下一班人。
這么一個場景,我們考慮該怎么實現(xiàn):
?
效果大概就是這個樣子:
CyclicBarrier 就像一個屏障,實例化的時候需要傳入兩個參數(shù),第一個參數(shù)指定我們的屏障最多攔截多少個線程后就打開屏障,第二個參數(shù)指明最后一個到達屏障的線程需要額外做的操作。
一般而言,最后一個線程到達屏障后,屏障將會打開,釋放前面所有的線程,并在最后重新關上屏障。
CyclicBarrier 只需要用到一個 await 就可以完成所有的功能,我們總結下該方法的實現(xiàn)邏輯:
首先,減少一次可用資源數(shù)量
如果可用資源數(shù)為零,則說明自己是最后一個線程,于是會執(zhí)行我們傳入的額外操作,喚醒所有已經到達在等待的線程,并重新開啟一個屏障計數(shù)。
否則說明自己不是最后一個線程,于是將自身線程在一個循環(huán)當中阻塞到一個條件隊列上
好了,看完 CyclicBarrier 你會發(fā)現(xiàn),它真的很類似我們的倒計時門栓,下面我們就來闡述他倆的區(qū)別與聯(lián)系。
?
第一個區(qū)別
倒計時門栓 CountDownLatch 一旦被打開后就不能再次合上,也是說只要被調用了足夠次數(shù)的 countDown,await 方法就會失效,它是一次性的。
CyclicBarrier 是循環(huán)發(fā)生的,當最后一個線程到達屏障,會優(yōu)先重置屏障計數(shù),屏障再次開啟攔截阻隔。
?
第二個區(qū)別
CountDownLatch 是計數(shù)器, 線程來一個就記一個,此期間不阻塞線程,當達到指定數(shù)量之后才會去喚醒外部等待的線程,也就是說外部是有一個乃至多個線程等待一個條件滿足之后才能繼續(xù)執(zhí)行,而這個條件就是滿足一定數(shù)量的線程,這樣才能激活當前外部線程的繼續(xù)執(zhí)行。
CyclicBarrier 像一個柵欄,來一個線程阻塞一個,直到阻塞了指定數(shù)量的線程后,一次性全部激活,讓他們同時執(zhí)行,像一個百米沖刺一樣。
?
最后的最后
好了,以上就是我們 Java 并發(fā)包下面比較好用的三個工具類,其中前兩個的底層實現(xiàn)幾乎完全依賴顯式鎖的原理方法,后一個則是使用的顯式鎖加條件變量重新造的輪子,都是非常好用的工具!
除此之外還要說一點的是,整個并發(fā)這塊內容,基本核心的東西我們都已經介紹完了,共計十四篇文章,從基本的線程概念,到鎖原理,到線程池,再到異步任務,自認為總結的足夠細致了,不知道你了解了多少呢?
記不住沒關系,我也為你提供了一份思維導圖的總結,羅列了上述基本的內容,你可以對照著進行回顧,同時也歡迎你私信我討論探究。
總結
以上是生活随笔為你收集整理的三个好用的并发工具类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果新专利获批:表带、服装等织物可变身柔
- 下一篇: 报告称微软 CEO 纳德拉今年收入 48