相信小夥伴對這個兩個詞或多或少都有些瞭解,他們是在併發編程中經常使用的線程通信工具。二者十分類似,可是又有不一樣,致使不少小夥伴也包括我在內產生了不少困惑:他們兩個究竟有什麼區別,以及適用於什麼場景呢?
下面聽我緩緩道來,不想看例子或者過程的小夥伴能夠拉到最下面看總結呦spring
閉鎖(CountDownLatch)坊間俗稱計數器,官方(谷歌機翻,哈哈)解釋:編程
/** * A synchronization aid that allows one or more threads to wait until * a set of operations being performed in other threads completes. */ 容許一個或多個線程等待,直到在其餘線程中執行的一組操做完成的同步輔助程序。
大概意思就是說,能夠有一個或者多個線程,等待其餘線程都完成某個操做後,再繼續執行。
什麼意思呢?舉個栗子吧:
生活中應該常常碰見一種狀況,坐公交車是,尤爲是始發站,司機師傅每每爲了一次拉更多的乘客,會等到車上乘客的數量到達必定程度之後纔會發車。測試代碼以下:segmentfault
public static void main(String[] args) { List<Passenger> list = new ArrayList<>(); Passenger p1 = new Passenger("看會書"); Passenger p2 = new Passenger("看會手機"); Passenger p3 = new Passenger("看會風景"); 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()); //設定閉鎖釋放閾值 CountDownLatch countDownLatch = new CountDownLatch(list.size()); log.error("司機師傅人夠一車再發車,等會人吧..."); 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("{}的乘客上車啦",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+"吧!"; } }
執行結果微信
23:46:34.698 [main] ERROR com.test - 司機師傅人夠一車再發車,等會人吧... 23:46:34.757 [zoo1] ERROR com.test - 看會手機的乘客上車啦 23:46:34.758 [zoo3] ERROR com.test - 看會售票員的乘客上車啦 23:46:34.757 [zoo0] ERROR com.test - 看會書的乘客上車啦 23:46:34.759 [zoo1] ERROR com.test - 車上好無聊啊,看會手機吧! 23:46:34.759 [zoo3] ERROR com.test - 車上好無聊啊,看會售票員吧! 23:46:34.757 [zoo2] ERROR com.test - 看會風景的乘客上車啦 23:46:34.759 [zoo0] ERROR com.test - 車上好無聊啊,看會書吧! 23:46:34.759 [zoo2] ERROR com.test - 車上好無聊啊,看會風景吧! 23:46:34.759 [main] ERROR com.test - 人夠了,起飛!
司機師傅(主線程)要等上了4個乘客之後才發車(等待4個子線程完成完成某件事之後調用countDown方法),而乘客上車(調用countDown)之後該作本身的事還作本身的事情,不會由於上了車就傻呆呆的什麼都不幹了(不會由於調用了countDown而阻塞自身)。等司機師傅看人夠了(到達設定閾值),就發車了。多線程
閉鎖總結:
主線程調用await後會阻塞等待其餘子線程調用countDown方法將設定閾值減至0,而後在繼續執行。
而子線程不會由於調用了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 * <em>cyclic</em> because it can be re-used after the waiting threads * are released. */ 同步幫助,容許一組線程互相等待,以達到共同的障礙點。 CyclicBarriers在涉及固定大小的線程方的程序中頗有用,這些線程有時必須互相等待。該屏障稱爲<em> cyclic </ em>,由於它能夠在釋放等待線程後從新使用。
從類註釋上咱們能夠大體瞭解到,他是運用在一組,也便是多個線程中的,當全部線程到達某個狀態前一直阻塞,直到全部線程都達到後再繼續執行。並且是能夠重複使用的。ide
上面的描述仍是太晦澀了,仍是舉個栗子:
咱們小時候學校都組織過春遊,規定好地點,等人到齊了就一塊兒進去玩。寫了個簡單的例子,看這種場景柵欄是怎麼工做的工具
public static void main(String[] args) { List<Boy> 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()); //初始化柵欄,設置障礙點閾值 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+"啦!"; } }
執行結果:測試
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 - 人到齊了,我要去看猩猩啦!
咱們能夠發現前三個小男孩在到達之後都沒有進到動物園裏,而是直到第四個小男孩來到之後,四個小男孩才進入動物園,在此以前每來一個小朋友就多一個小朋友等待(每一個線程調用await方法),直到等待全部人到齊(線程阻塞等待達到柵欄障礙點4),各個小男孩再去繼續進入動物園看動物(各線程繼續執行本身的任務)。就像是動物園大門的柵欄,買的是團體票,每次必須人到齊才放開讓小朋友進去同樣。this
柵欄總結
各子線程相互等待,直到達到柵欄初始化時的閾值,則繼續執行
閉鎖:有點相似於一個統計功能(可能這也是爲何他俗稱計數器),主線程調用await方法阻塞等待統計結果,而子線程只負責在達到統計要求時調用countDown方法告訴主線程我好了,而不會阻塞自己;有一個負責接收結果(主線程)和一個或多個發送數量的(子線程);
柵欄:首先在線程調用await方法時會阻塞當前線程,其次我的理解他沒有相似像閉鎖那樣的主子的關係,他是各個線程相互等待,都到達某個點的時候,則繼續執行。
其實從上面的區分就能看出一些:若是是須要將多線程執行完成與否的接口彙總到某一個線程中,而後再繼續執行的狀況,好比每條線程計算一個指標,都計算完成之後再計算全部指標的總和或者其餘的,就可使用閉鎖;
而若是隻是各個線程須要等各個線程都完成了,再繼續本身的事,可使用柵欄,好比ABC三個線程分別去獲取123三個指標,而後再A要取這三個數的平均數,B要取總和,C要取方差,那就須要等ABC都先取完了123這三個指標,才能計算,這時候就能夠用到柵欄了。
這兩種都是很是好的線程通信工具,不過細節仍是有所差別。
總得來講就是:
閉鎖是爲了在 某一條線程等待獲取到 其餘線程的執行結果;
而柵欄則是線程間的 相互等待,而後再同時開始作 各自的事情
文中的代碼只是爲了比較好的說明兩種工具的差別,寫的很差還請小夥伴們多多包涵,若是發現有哪點寫的不對的也歡迎你們夥們留言,咱們共同進步!最後若是小夥伴以爲文章不錯,不妨動動小手點個贊再走,不要下次必定呦~
給走過路過的小夥伴們推薦一個公衆號,」菜鳥封神記「,裏面幹活滿滿,最新在更新spring源碼相關的文章,感興趣的小夥伴不妨關注一下,微信搜索公衆號:」菜鳥封神記「,或者掃描下面公衆號關注便可。