新聞中心
開篇

創(chuàng)新互聯(lián)一直在為企業(yè)提供服務(wù),多年的磨煉,使我們在創(chuàng)意設(shè)計(jì),營銷型網(wǎng)站建設(shè)到技術(shù)研發(fā)擁有了開發(fā)經(jīng)驗(yàn)。我們擅長傾聽企業(yè)需求,挖掘用戶對產(chǎn)品需求服務(wù)價(jià)值,為企業(yè)制作有用的創(chuàng)意設(shè)計(jì)體驗(yàn)。核心團(tuán)隊(duì)擁有超過十多年以上行業(yè)經(jīng)驗(yàn),涵蓋創(chuàng)意,策化,開發(fā)等專業(yè)領(lǐng)域,公司涉及領(lǐng)域有基礎(chǔ)互聯(lián)網(wǎng)服務(wù)成都移動云計(jì)算中心、成都app開發(fā)、手機(jī)移動建站、網(wǎng)頁設(shè)計(jì)、網(wǎng)絡(luò)整合營銷。
相信小伙伴對這個(gè)兩個(gè)詞或多或少都有些了解,他們是在并發(fā)編程中常用的線程通訊工具。兩者十分相似,但是又有不同,導(dǎo)致很多小伙伴也包括我在內(nèi)產(chǎn)生了很多困惑:他們兩個(gè)究竟有什么區(qū)別,以及適用于什么場景呢?
下面聽我緩緩道來,不想看例子或者過程的小伙伴可以拉到最下面看總結(jié)呦
閉鎖
閉鎖(CountDownLatch)坊間俗稱計(jì)數(shù)器,官方(谷歌機(jī)翻,哈哈)解釋:
- /**
- * A synchronization aid that allows one or more threads to wait until
- * a set of operations being performed in other threads completes.
- */
- 允許一個(gè)或多個(gè)線程等待,直到在其他線程中執(zhí)行的一組操作完成的同步輔助程序。
大概意思就是說,可以有一個(gè)或者多個(gè)線程,等待其他線程都完成某個(gè)操作后,再繼續(xù)執(zhí)行。
什么意思呢?舉個(gè)栗子吧:
生活中應(yīng)該經(jīng)常遇見一種情況,坐公交車是,尤其是始發(fā)站,司機(jī)師傅往往為了一次拉更多的乘客,會等到車上乘客的數(shù)量到達(dá)一定程度以后才會發(fā)車。測試代碼如下:
- public static void main(String[] args) {
- List
list = new ArrayList<>(); - Passenger p1 = new Passenger("看會書");
- Passenger p2 = new Passenger("看會手機(jī)");
- Passenger p3 = new Passenger("看會風(fēng)景");
- Passenger p4 = new Passenger("看會售票員");
- list.add(p1);
- list.add(p2);
- list.add(p3);
- list.add(p4);
- ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 200, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadFactory() {
- private ThreadGroup group = (null == System.getSecurityManager() ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());
- private AtomicInteger num = new AtomicInteger();
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(group, r,"zoo" + num.getAndIncrement(),0);
- thread.setDaemon(false);
- return thread;
- }
- }, new ThreadPoolExecutor.CallerRunsPolicy());
- //設(shè)定閉鎖釋放閾值
- CountDownLatch countDownLatch = new CountDownLatch(list.size());
- log.error("司機(jī)師傅人夠一車再發(fā)車,等會人吧...");
- for (Passenger p : list) {
- executor.execute(()->gotoZOO(p,countDownLatch));
- }
- try {
- countDownLatch.await();
- log.error("人夠了,起飛!");
- executor.shutdown();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- private static void gotoZOO(Passenger p,CountDownLatch countDownLatch){
- log.error("{}的乘客上車?yán)?,p.getDoWhat());
- try {
- countDownLatch.countDown();
- log.error("{}",p.doWhatOnBus());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- static class Passenger{
- private String doWhat;
- public Passenger(String doWhat) {
- this.doWhat = doWhat;
- }
- public String getDoWhat() {
- return doWhat;
- }
- public String doWhatOnBus() {
- return "車上好無聊啊,"+doWhat+"吧!";
- }
- }
執(zhí)行結(jié)果
- 23:46:34.698 [main] ERROR com.test - 司機(jī)師傅人夠一車再發(fā)車,等會人吧...
- 23:46:34.757 [zoo1] ERROR com.test - 看會手機(jī)的乘客上車?yán)?nbsp;
- 23:46:34.758 [zoo3] ERROR com.test - 看會售票員的乘客上車?yán)?nbsp;
- 23:46:34.757 [zoo0] ERROR com.test - 看會書的乘客上車?yán)?nbsp;
- 23:46:34.759 [zoo1] ERROR com.test - 車上好無聊啊,看會手機(jī)吧!
- 23:46:34.759 [zoo3] ERROR com.test - 車上好無聊啊,看會售票員吧!
- 23:46:34.757 [zoo2] ERROR com.test - 看會風(fēng)景的乘客上車?yán)?nbsp;
- 23:46:34.759 [zoo0] ERROR com.test - 車上好無聊啊,看會書吧!
- 23:46:34.759 [zoo2] ERROR com.test - 車上好無聊啊,看會風(fēng)景吧!
- 23:46:34.759 [main] ERROR com.test - 人夠了,起飛!
司機(jī)師傅(主線程)要等上了4個(gè)乘客以后才發(fā)車(等待4個(gè)子線程完成完成某件事以后調(diào)用countDown方法),而乘客上車(調(diào)用countDown)以后該做自己的事還做自己的事情,不會因?yàn)樯狭塑嚲蜕荡舸舻氖裁炊疾桓闪?不會因?yàn)檎{(diào)用了countDown而阻塞自身)。等司機(jī)師傅看人夠了(到達(dá)設(shè)定閾值),就發(fā)車了。
閉鎖總結(jié):
- 主線程調(diào)用await后會阻塞等待其他子線程調(diào)用countDown方法將設(shè)定閾值減至0,然后在繼續(xù)執(zhí)行。
- 而子線程不會因?yàn)檎{(diào)用了countDown方法而阻塞
柵欄
柵欄(CyclicBarrier)官方解釋:
- /**
- * A synchronization aid that allows a set of threads to all wait for
- * each other to reach a common barrier point. CyclicBarriers are
- * useful in programs involving a fixed sized party of threads that
- * must occasionally wait for each other. The barrier is called
- * cyclic because it can be re-used after the waiting threads
- * are released.
- */
- 同步幫助,允許一組線程互相等待,以達(dá)到共同的障礙點(diǎn)。 CyclicBarriers在涉及固定大小的線程方的程序中很有用,這些線程有時(shí)必須互相等待。該屏障稱為 cyclic em>,因?yàn)樗梢栽卺尫诺却€程后重新使用。
從類注釋上我們可以大致了解到,他是運(yùn)用在一組,也即是多個(gè)線程中的,當(dāng)所有線程到達(dá)某個(gè)狀態(tài)前一直阻塞,直到所有線程都達(dá)到后再繼續(xù)執(zhí)行。而且是可以重復(fù)使用的。
上面的描述還是太晦澀了,還是舉個(gè)栗子:
我們小時(shí)候?qū)W校都組織過春游,規(guī)定好地點(diǎn),等人到齊了就一起進(jìn)去玩。寫了個(gè)簡單的例子,看這種場景柵欄是怎么工作的
- public static void main(String[] args) {
- List
list = new ArrayList<>(); - Boy boy1 = new Boy("看老虎");
- Boy boy2 = new Boy("看猩猩");
- Boy boy3 = new Boy("看獅子");
- Boy boy4 = new Boy("看售票員");
- list.add(boy1);
- list.add(boy2);
- list.add(boy3);
- list.add(boy4);
- ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 200, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), new ThreadFactory() {
- private ThreadGroup group = (null == System.getSecurityManager() ? Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());
- private AtomicInteger num = new AtomicInteger();
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(group, r,"zoo" + num.getAndIncrement(),0);
- thread.setDaemon(false);
- return thread;
- }
- }, new ThreadPoolExecutor.CallerRunsPolicy());
- //初始化柵欄,設(shè)置障礙點(diǎn)閾值
- CyclicBarrier cyclicBarrier = new CyclicBarrier(list.size());
- for (Boy boy : list) {
- executor.execute(()->gotoZOO(boy,cyclicBarrier));
- }
- }
- private static void gotoZOO(Boy boy,CyclicBarrier cyclicBarrier){
- log.error("人還沒到齊呢,等一下吧,{}的小男孩開始等待",boy.getWhere());
- try {
- cyclicBarrier.await();
- log.error("{}",boy.goWhere());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- static class Boy{
- private String where;
- public Boy(String where) {
- this.where = where;
- }
- public String getWhere() {
- return where;
- }
- public String goWhere() {
- return "人到齊了,我要去"+where+"啦!";
- }
- }
執(zhí)行結(jié)果:
- 22:05:59.476 [zoo2] ERROR com.test - 人還沒到齊呢,等一下吧,看獅子的小男孩開始等待
- 22:05:59.477 [zoo1] ERROR com.test - 人還沒到齊呢,等一下吧,看猩猩的小男孩開始等待
- 22:05:59.477 [zoo0] ERROR com.test - 人還沒到齊呢,等一下吧,看老虎的小男孩開始等待
- 22:05:59.476 [zoo3] ERROR com.test - 人還沒到齊呢,等一下吧,看售票員的小男孩開始等待
- 22:05:59.484 [zoo0] ERROR com.test - 人到齊了,我要去看老虎啦!
- 22:05:59.484 [zoo2] ERROR com.test - 人到齊了,我要去看獅子啦!
- 22:05:59.484 [zoo3] ERROR com.test - 人到齊了,我要去看售票員啦!
- 22:05:59.484 [zoo1] ERROR com.test - 人到齊了,我要去看猩猩啦!
我們可以發(fā)現(xiàn)前三個(gè)小男孩在到達(dá)以后都沒有進(jìn)到動物園里,而是直到第四個(gè)小男孩來到以后,四個(gè)小男孩才進(jìn)入動物園,在此之前每來一個(gè)小朋友就多一個(gè)小朋友等待(每個(gè)線程調(diào)用await方法),直到等待所有人到齊(線程阻塞等待達(dá)到柵欄障礙點(diǎn)4),各個(gè)小男孩再去繼續(xù)進(jìn)入動物園看動物(各線程繼續(xù)執(zhí)行自己的任務(wù))。就像是動物園大門的柵欄,買的是團(tuán)體票,每次必須人到齊才放開讓小朋友進(jìn)去一樣。
柵欄總結(jié)
各子線程相互等待,直到達(dá)到柵欄初始化時(shí)的閾值,則繼續(xù)執(zhí)行
區(qū)分以及個(gè)人理解
閉鎖:有點(diǎn)類似于一個(gè)統(tǒng)計(jì)功能(可能這也是為什么他俗稱計(jì)數(shù)器),主線程調(diào)用await方法阻塞等待統(tǒng)計(jì)結(jié)果,而子線程只負(fù)責(zé)在達(dá)到統(tǒng)計(jì)要求時(shí)調(diào)用countDown方法告訴主線程我好了,而不會阻塞本身;有一個(gè)負(fù)責(zé)接收結(jié)果(主線程)和一個(gè)或多個(gè)發(fā)送數(shù)量的(子線程);
柵欄:首先在線程調(diào)用await方法時(shí)會阻塞當(dāng)前線程,其次個(gè)人理解他沒有類似像閉鎖那樣的主子的關(guān)系,他是各個(gè)線程相互等待,都到達(dá)某個(gè)點(diǎn)的時(shí)候,則繼續(xù)執(zhí)行。
適用場景
其實(shí)從上面的區(qū)分就能看出一些:如果是需要將多線程執(zhí)行完成與否的接口匯總到某一個(gè)線程中,然后再繼續(xù)執(zhí)行的情況,比如每條線程計(jì)算一個(gè)指標(biāo),都計(jì)算完成以后再計(jì)算所有指標(biāo)的總和或者其他的,就可以使用閉鎖;
而如果只是各個(gè)線程需要等各個(gè)線程都完成了,再繼續(xù)自己的事,可以使用柵欄,比如ABC三個(gè)線程分別去獲取123三個(gè)指標(biāo),然后再A要取這三個(gè)數(shù)的平均數(shù),B要取總和,C要取方差,那就需要等ABC都先取完了123這三個(gè)指標(biāo),才能計(jì)算,這時(shí)候就可以用到柵欄了。
總結(jié)
這兩種都是非常好的線程通訊工具,不過細(xì)節(jié)還是有所差異。
總得來說就是:
- 閉鎖是為了在某一條線程等待獲取到其他線程的執(zhí)行結(jié)果;
- 而柵欄則是線程間的相互等待,然后再同時(shí)開始做各自的事情
最后
文中的代碼只是為了比較好的說明兩種工具的差異,寫的不好還請小伙伴們多多包涵,如果發(fā)現(xiàn)有哪點(diǎn)寫的不對的也歡迎大家伙們留言,我們共同進(jìn)步!最后如果小伙伴覺得文章不錯(cuò),不妨動動小手點(diǎn)個(gè)贊再走,不要下次一定呦~
文章名稱:閉鎖和柵欄的區(qū)分以及適用場景
標(biāo)題網(wǎng)址:http://fisionsoft.com.cn/article/cdhgjhs.html


咨詢
建站咨詢
