轉自:http://www.importnew.com/15731.html
正如每個Java文檔所描述的那樣,CountDownLatch是一個同步工具類,它允許一個或多個線程一直等待,直到其他線程的操作執行完後再執行。在Java併發中,countdownlatch的概念是一個常見的面試題,所以一定要確保你很好的理解了它。在這篇文章中,我將會涉及到在Java併發編 程中跟CountDownLatch相關的以下幾點:
目錄
- CountDownLatch是什麼?
- CountDownLatch如何工作?
- 在實時系統中的應用場景
- 應用範例
- 常見的面試題
CountDownLatch是什麼
CountDownLatch是在java1.5被引入的,跟它一起被引入的併發工具類還有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它們都存在於java.util.concurrent包下。CountDownLatch這個類能夠使一個線程等待其他線程完成各自的工作後再執行。例如,應用程序的主線程希望在負責啓動框架服務的線程已經啓動所有的框架服務之後再執行。
CountDownLatch是通過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了自己的任務後,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。
CountDownLatch的僞代碼如下所示:
1
2
3
4
5
6
|
//Main
thread start //Create
CountDownLatch for N threads //Create
and start N threads //Main
thread wait on latch //N
threads completes there tasks are returns //Main
thread resume execution |
CountDownLatch如何工作
CountDownLatch.java類中定義的構造函數:
1
2
|
//Constructs
a CountDownLatch initialized with the given count. public
void
CountDownLatch( int
count) {...} |
構造器中的計數值(count)實際上就是閉鎖需要等待的線程數量。這個值只能被設置一次,而且CountDownLatch沒有提供任何機制去重新設置這個計數值。
與CountDownLatch的第一次交互是主線程等待其他線程。主線程必須在啓動其他線程後立即調用CountDownLatch.await()方法。這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務。
其他N 個線程必須引用閉鎖對象,因爲他們需要通知CountDownLatch對象,他們已經完成了各自的任務。這種通知機制是通過 CountDownLatch.countDown()方法來完成的;每調用一次這個方法,在構造函數中初始化的count值就減1。所以當N個線程都調 用了這個方法,count的值等於0,然後主線程就能通過await()方法,恢復執行自己的任務。
在實時系統中的使用場景
讓我們嘗試羅列出在java實時系統中CountDownLatch都有哪些使用場景。我所羅列的都是我所能想到的。如果你有別的可能的使用方法,請在留言裏列出來,這樣會幫助到大家。
- 實現最大的並行性:有時我們想同時啓動多個線程,實現最大程度的並行性。例如,我們想測試一個單例類。如果我們創建一個初始計數爲1的CountDownLatch,並讓所有線程都在這個鎖上等待,那麼我們可以很輕鬆地完成測試。我們只需調用 一次countDown()方法就可以讓所有的等待線程同時恢復執行。
- 開始執行前等待n個線程完成各自任務:例如應用程序啓動類要確保在處理用戶請求前,所有N個外部系統已經啓動和運行了。
- 死鎖檢測:一個非常方便的使用場景是,你可以使用n個線程訪問共享資源,在每次測試階段的線程數目是不同的,並嘗試產生死鎖。
CountDownLatch使用例子
在這個例子中,我模擬了一個應用程序啓動類,它開始時啓動了n個線程類,這些線程將檢查外部系統並通知閉鎖,並且啓動類一直在閉鎖上等待着。一旦驗證和檢查了所有外部服務,那麼啓動類恢復執行。
BaseHealthChecker.java:這個類是一個Runnable,負責所有特定的外部服務健康的檢測。它刪除了重複的代碼和閉鎖的中心控制代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
public
abstract
class
BaseHealthChecker implements
Runnable { private
CountDownLatch _latch; private
String _serviceName; private
boolean
_serviceUp; //Get
latch object in constructor so that after completing the task, thread can countDown() the latch public
BaseHealthChecker(String serviceName, CountDownLatch latch) { super (); this ._latch
= latch; this ._serviceName
= serviceName; this ._serviceUp
= false ; } @Override public
void
run() { try
{ verifyService(); _serviceUp
= true ; }
catch
(Throwable t) { t.printStackTrace(System.err); _serviceUp
= false ; }
finally
{ if (_latch
!= null )
{ _latch.countDown(); } } } public
String getServiceName() { return
_serviceName; } public
boolean
isServiceUp() { return
_serviceUp; } //This
methos needs to be implemented by all specific service checker public
abstract
void
verifyService(); } |
NetworkHealthChecker.java:這個類繼承了BaseHealthChecker,實現了verifyService()方法。DatabaseHealthChecker.java和CacheHealthChecker.java除了服務名和休眠時間外,與NetworkHealthChecker.java是一樣的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
NetworkHealthChecker extends
BaseHealthChecker { public
NetworkHealthChecker (CountDownLatch latch) { super ( "Network
Service" ,
latch); } @Override public
void
verifyService() { System.out.println( "Checking
"
+ this .getServiceName()); try { Thread.sleep( 7000 ); } catch
(InterruptedException e) { e.printStackTrace(); } System.out.println( this .getServiceName()
+ "
is UP" ); } } |
ApplicationStartupUtil.java:這個類是一個主啓動類,它負責初始化閉鎖,然後等待,直到所有服務都被檢測完。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
public
class
ApplicationStartupUtil { //List
of service checkers private
static
List<BaseHealthChecker> _services; //This
latch will be used to wait on private
static
CountDownLatch _latch; private
ApplicationStartupUtil() { } private
final
static
ApplicationStartupUtil INSTANCE = new
ApplicationStartupUtil(); public
static
ApplicationStartupUtil getInstance() { return
INSTANCE; } public
static
boolean
checkExternalServices() throws
Exception { //Initialize
the latch with number of service checkers _latch
= new
CountDownLatch( 3 ); //All
add checker in lists _services
= new
ArrayList<BaseHealthChecker>(); _services.add( new
NetworkHealthChecker(_latch)); _services.add( new
CacheHealthChecker(_latch)); _services.add( new
DatabaseHealthChecker(_latch)); //Start
service checkers using executor framework Executor
executor = Executors.newFixedThreadPool(_services.size()); for ( final
BaseHealthChecker v : _services) { executor.execute(v); } //Now
wait till all services are checked _latch.await(); //Services
are file and now proceed startup for ( final
BaseHealthChecker v : _services) { if (
! v.isServiceUp()) { return
false ; } } return
true ; } } |
現在你可以寫測試代碼去檢測一下閉鎖的功能了。
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
Main { public
static
void
main(String[] args) { boolean
result = false ; try
{ result
= ApplicationStartupUtil.checkExternalServices(); }
catch
(Exception e) { e.printStackTrace(); } System.out.println( "External
services validation completed !! Result was :: " +
result); } } |
1
2
3
4
5
6
7
8
9
|
Output
in
console: Checking
Network Service Checking
Cache Service Checking
Database Service Database
Service is UP Cache
Service is UP Network
Service is UP External
services validation completed !! Result was :: true |
常見面試題
可以爲你的下次面試準備以下一些CountDownLatch相關的問題:
- 解釋一下CountDownLatch概念?
- CountDownLatch 和CyclicBarrier的不同之處?
- 給出一些CountDownLatch使用的例子?
- CountDownLatch 類中主要的方法?