一、倒計時CountDownLatch

CountDownLatch是一個非常實用的多線程控制工具類,稱之爲“倒計時器”,它允許一個或多個線程一直等待,直到其他線程的操作執行完後再執行。

舉了例子:

我們知道的集齊七顆龍珠就可以召喚神龍,那我們就一起召喚一下,下邊我需要派7個人(7個線程)去分別去找這7顆不同的龍珠,每個人找到之後回來告訴我還需要等待的龍珠個數減1個,那麼當全部的人都找到龍珠之後,那麼我就可以召喚神龍了。

順便寫個代碼如下:

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

運行結果如下:

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

上述的執行結果可以看出,當分配的7個人(7個線程)分別找到龍珠之後,也就是所有的線程執行完畢,纔可以召喚龍珠(執行countDownLatch.await()之後的代碼)。

注意:

(1)CountDownLatch的構造函數

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

7表示需要等待執行完畢的線程數量。

(2)在每一個線程執行完畢之後,都需要執行countDownLatch.countDown()方法,不然計數器就不會準確;

(3)只有所有的線程執行完畢之後,纔會執行 countDownLatch.await()之後的代碼;

(4)可以看出上述代碼中CountDownLatch 阻塞的是主線程;

那麼,假如我們不是用計數器CountDownLatch的話,結果可想而知,示例如下:

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

結果只能呵呵了!

好啦!上邊說了一堆水話,下面說點官方的解釋:

CountDownLatch是在java1.5被引入的,它存在於java.util.concurrent包下。CountDownLatch這個類能夠使一個線程等待其他線程完成各自的工作後再執行。例如,應用程序的主線程希望在負責啓動框架服務的線程已經啓動所有的框架服務之後再執行。

CountDownLatch是通過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

CountDownLatch.java類中定義的構造函數:

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

構造器中的計數值(count)實際上就是閉鎖需要等待的線程數量。這個值只能被設置一次,而且CountDownLatch沒有提供任何機制去重新設置這個計數值。

與CountDownLatch的第一次交互是主線程等待其他線程。主線程必須在啓動其他線程後立即調用CountDownLatch.await()方法。這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務。

其他N 個線程必須引用閉鎖對象,因爲他們需要通知CountDownLatch對象,他們已經完成了各自的任務。這種通知機制是通過 CountDownLatch.countDown()方法來完成的;每調用一次這個方法,在構造函數中初始化的count值就減1。所以當N個線程都調用了這個方法,count的值等於0,然後主線程就能通過await()方法,恢復執行自己的任務。

二、CountDownLatch在實時系統中的使用場景

讓我們嘗試羅列出在java實時系統中CountDownLatch都有哪些使用場景。我所羅列的都是我所能想到的。如果你有別的可能的使用方法,請在留言裏列出來,這樣會幫助到大家。

(1)實現最大的並行性:有時我們想同時啓動多個線程,實現最大程度的並行性。例如,我們想測試一個單例類。如果我們創建一個初始計數爲1的CountDownLatch,並讓所有線程都在這個鎖上等待,那麼我們可以很輕鬆地完成測試。我們只需調用 一次countDown()方法就可以讓所有的等待線程同時恢復執行。

(2)開始執行前等待n個線程完成各自任務:例如應用程序啓動類要確保在處理用戶請求前,所有N個外部系統已經啓動和運行了。

(3)死鎖檢測:一個非常方便的使用場景是,你可以使用n個線程訪問共享資源,在每次測試階段的線程數目是不同的,並嘗試產生死鎖。

三、循環屏障CyclicBarrier

CyclicBarrier是另一種多線程併發控制使用工具,和CountDownLatch非常類似,他也可以實現線程間的計數等待,但他的功能要比CountDownLatch更加強大一些。

CyclicBarrier 的字面意思是可循環使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被屏障攔截的線程纔會繼續幹活。

CyclicBarrier默認的構造方法是CyclicBarrier(int parties),其參數表示屏障攔截的線程數量,每個線程調用await方法告訴CyclicBarrier我已經到達了屏障,然後當前線程被阻塞。

CyclicBarrier強調的是n個線程,大家相互等待,只要有一個沒完成,所有人都得等着。

還接着上述“集齊七顆龍珠!召喚神龍”的故事。召喚神龍,需要7個法師去尋找龍珠,但這7個法師並不是一下子就能號召起來的,所以要等待召集齊7個法師,然後在秋名山頂燒香拜佛爲這7位法師送行,讓他們同時出發,前往不同的地方尋找龍珠(敲黑板:這是第一個屏障點),在這七位法師臨行時約定找到龍珠之後還回到這個地方等待其他法師找到龍珠之後一起去見我。幾年之後,第一個法師回來了,然後等待其他的法師。。。,最後所有的法師全部到齊(敲黑板:這是第一個屏障點),然後組隊來找我召喚神龍。

示例代碼如下:

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

執行結果:

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

代碼中設置了兩個屏障點,第一個用於召集7個法師,等7個法師召集完後,在設置在一個屏障點,7位法師去尋找龍珠,然後召喚神龍,中間有個嵌套的關係!

上述的例子,大致說了一下屏障,因爲設置了兩個屏障,並沒有演示上述說的可循環使用(Cyclic)的屏障(Barrier) 中的可循環使用(Cyclic)

兩種常用的線程計數器CountDownLatch和循環屏障CyclicBarrier

查看CyclicBarrier.reset() 可知,可以使CyclicBarrier回到最初始的狀態,由於使用的相對較少,這裏不再演示。

四、CyclicBarrier和CountDownLatch的區別

(1)CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重置。所以CyclicBarrier能處理更爲複雜的業務場景,比如如果計算髮生錯誤,可以重置計數器,並讓線程們重新執行一次。

(2)CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數量。isBroken方法用來知道阻塞的線程是否被中斷。比如以下代碼執行完之後會返回true。

(3)CountDownLatch會阻塞主線程,CyclicBarrier不會阻塞主線程,只會阻塞子線程。

參考文章:

1、http://www.importnew.com/15731.html

相關文章