CountDownLatch主要用于同步一个或多个任务,强制它们等待由其他任务执行的一组操作完成。
你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用wait()的方法都将阻塞,直到这个计数值达到0.其他任务在结束其工作时,可以在该对象上调用countDown()来减小这个计数值,你可以通过调用getCount()方法来获取当前的计数值。CountDownLatch被设计为只触发一次,计数值不能被重置。如果你需要能够重置计数值的版本,则可以使用CyclicBarrier。
调用countDown()的任务在产生这个调用时并没有阻塞,只有对await()的调用会被阻塞,直到计数值到达0。
CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决人物,并创建值为0的CountDownLatch。当每个任务完成是,都会在这个锁存器上调用countDown()。等待问题被解决的任务在这个锁存器上调用await(),将它们自己锁住,直到锁存器计数结束。下面是演示这种技术的一个框架示例:
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
import
java.util.Random;
import
java.util.concurrent.CountDownLatch;
import
java.util.concurrent.DelayQueue;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
java.util.concurrent.TimeUnit;
class
TaskPortion
implements
Runnable {
private
static
int
counter =
0
;
private
final
int
id = counter++;
private
static
Random random =
new
Random();
private
final
CountDownLatch latch;
public
TaskPortion(CountDownLatch latch) {
this
.latch = latch;
}
@Override
public
void
run() {
try
{
doWork();
latch.countDown();
//普通任务执行完后,调用countDown()方法,减少count的值
System.out.println(
this
+
" completed. count="
+ latch.getCount());
}
catch
(InterruptedException e) {
}
}
public
void
doWork()
throws
InterruptedException {
TimeUnit.MILLISECONDS.sleep(random.nextInt(
2000
));
}
@Override
public
String toString() {
return
String.format(
"%1$-2d "
, id);
}
}
class
WaitingTask
implements
Runnable {
private
static
int
counter =
0
;
private
final
int
id = counter++;
private
final
CountDownLatch latch;
public
WaitingTask(CountDownLatch latch) {
this
.latch = latch;
}
@Override
public
void
run() {
try
{
//这些后续任务需要等到之前的任务都执行完成后才能执行,即count=0时
latch.await();
System.out.println(
"Latch barrier passed for "
+
this
);
}
catch
(InterruptedException e) {
System.out.println(
this
+
" interrupted."
);
}
}
@Override
public
String toString() {
return
String.format(
"WaitingTask %1$-2d "
, id);
}
}
public
class
CountDownLatchDemo {
static
final
int
SIZE =
10
;
public
static
void
main(String[] args) {
CountDownLatch latch =
new
CountDownLatch(SIZE);
ExecutorService exec = Executors.newCachedThreadPool();
//10个WaitingTask
for
(
int
i =
0
; i <
5
; i++) {
exec.execute(
new
WaitingTask(latch));
}
//100个任务,这100个任务要先执行才会执行WaitingTask
for
(
int
i =
0
; i < SIZE; i++) {
exec.execute(
new
TaskPortion(latch));
}
System.out.println(
"Launched all tasks."
);
exec.shutdown();
//当所有的任务都结束时,关闭exec
}
}
|
执行结果(可能的结果):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
Launched all tasks.
4
completed. count=
9
6
completed. count=
8
3
completed. count=
7
0
completed. count=
6
2
completed. count=
5
1
completed. count=
4
5
completed. count=
3
7
completed. count=
2
9
completed. count=
1
8
completed. count=
0
Latch barrier passed
for
WaitingTask
0
Latch barrier passed
for
WaitingTask
2
Latch barrier passed
for
WaitingTask
1
Latch barrier passed
for
WaitingTask
3
Latch barrier passed
for
WaitingTask
4
|
从结果中可以看到,所有的WaitingTask都是在所有的TaskPortion执行完成之后执行的。
TaskPortion将随机的休眠一段时间,以模拟这部分工作的完成。而WaitingTask表示系统中必须等待的部分,它要等到问题的初始部分完成后才能执行。注意:所有任务都使用了在main()中定义的同一个CountDownLatch对象。