正如每一個Java文檔所描述的那樣,CountDownLatch是一個同步工具類,它容許一個或多個線程一直等待,直到其餘線程的操做執行完後再執行。在Java併發中,countdownlatch的概念是一個常見的面試題,因此必定要確保你很好的理解了它。在這篇文章中,我將會涉及到在Java併發編 程中跟CountDownLatch相關的如下幾點:html
CountDownLatch是在java1.5被引入的,跟它一塊兒被引入的併發工具類還有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它們都存在於java.util.concurrent包下。CountDownLatch這個類可以使一個線程等待其餘線程完成各自的工做後再執行。例如,應用程序的主線程但願在負責啓動框架服務的線程已經啓動全部的框架服務以後再執行。java
CountDownLatch是經過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了本身的任務後,計數器的值就會減1。當計數器值到達0時,它表示全部的線程已經完成了任務,而後在閉鎖上等待的線程就能夠恢復執行任務。面試
CountDownLatch的僞代碼以下所示:shell
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.java類中定義的構造函數:api
1
2
|
//Constructs a CountDownLatch initialized with the given count.
public
void
CountDownLatch(
int
count) {...}
|
構造器中的計數值(count)實際上就是閉鎖須要等待的線程數量。這個值只能被設置一次,並且CountDownLatch沒有提供任何機制去從新設置這個計數值。併發
與CountDownLatch的第一次交互是主線程等待其餘線程。主線程必須在啓動其餘線程後當即調用CountDownLatch.await()方法。這樣主線程的操做就會在這個方法上阻塞,直到其餘線程完成各自的任務。oracle
其餘N 個線程必須引用閉鎖對象,由於他們須要通知CountDownLatch對象,他們已經完成了各自的任務。這種通知機制是經過 CountDownLatch.countDown()方法來完成的;每調用一次這個方法,在構造函數中初始化的count值就減1。因此當N個線程都調 用了這個方法,count的值等於0,而後主線程就能經過await()方法,恢復執行本身的任務。框架
讓咱們嘗試羅列出在java實時系統中CountDownLatch都有哪些使用場景。我所羅列的都是我所能想到的。若是你有別的可能的使用方法,請在留言裏列出來,這樣會幫助到你們。ide
在這個例子中,我模擬了一個應用程序啓動類,它開始時啓動了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相關的問題: