CountDownLatch和CyclicBarrier的簡單使用

小飛鶴發表於2017-08-30

轉自:http://www.kissyu.org/2016/07/18/%E5%AD%A6%E4%B9%A0%E4%BD%BF%E7%94%A8CountDownLatch%E5%92%8CCyclicBarrier/

簡介

CountDownLatch和CyclicBarrier都是執行緒同步的輔助工具。

CountDownLatch

CountDownLatch可以想象成一個上了N把鎖的門栓。當某個執行緒呼叫await方法時,它會去檢測當前門栓上是否有鎖,如果有鎖的話就繼續執行,否則就繼續等待,知道全部的鎖都被解除。可以通過呼叫countDown方法來從門栓上解除一把鎖。

有一個十分典型的應用場景就是利用CountDownLatch來保證執行緒同時開始執行或者保證當前執行緒等待其他執行緒全部結束之後繼續執行,程式碼如下:

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
public class TestDemo {
public static void main(String args[]) throws InterruptedException {
int threadCount = 10;
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
// create and start threads
new Thread(new Worker(startSignal, doneSignal, "th-" + i)).start();
}
System.out.println("sub threads waiting for main thread");
startSignal.countDown(); // let all threads proceed
doneSignal.await(); // wait for all to finish
System.out.println("sub threads complete");
}
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
private final String name;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal, String name) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
this.name = name;
}
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
}
catch (InterruptedException ex) {
} // return;
}
void doWork() {
System.out.println(this.name + " is doing work");
}
}
}

執行結果如下:

1
2
3
4
5
6
7
8
9
10
11
12
sub threads waiting for main thread
th-0 is doing work
th-3 is doing work
th-4 is doing work
th-1 is doing work
th-2 is doing work
th-9 is doing work
th-8 is doing work
th-7 is doing work
th-5 is doing work
th-6 is doing work
sub threads complete

CyclicBarrier

我們可以用學校春遊之前在操場集合的這個場景來理解CyclicBarrier。一個班級如果要出發,那麼必須要等班裡所有的人都到了才能走。到了集合地點的人需要簽到,然後等待其他人,這個過程就像CyclicBarrier中的await方法。當所有人都簽到完畢,班主任會上前說一些注意事項,這一步就像下面程式碼中的barrierAction。然後,所有人才可以出發,在程式碼中的表現就是繼續執行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
public class TestDemo {
public static void main(String args[]) throws InterruptedException {
int threadCount = 10;
Runnable barrierAction = new Runnable() {
public void run() {
System.out.println("sub threads complete");
}
};
CyclicBarrier barrier = new CyclicBarrier(threadCount, barrierAction);
List<Thread> threads = new ArrayList<>(threadCount);
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new Worker(barrier, "th-" + i));
threads.add(thread);
thread.start();
}
// wait until done
for (Thread thread : threads) {
thread.join();
}
}
public static class Worker implements Runnable {
private CyclicBarrier barrier;
private String name;
Worker(CyclicBarrier barrier, String name) {
this.barrier = barrier;
this.name = name;
}
public void run() {
try {
doWork();
barrier.await();
System.out.println(name + " waiting finish");
}
catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
private void doWork() {
System.out.println(name + " is doing work");
}
}
}

執行結果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
th-0 is doing work
th-1 is doing work
th-2 is doing work
th-3 is doing work
th-4 is doing work
th-5 is doing work
th-6 is doing work
th-7 is doing work
th-8 is doing work
th-9 is doing work
sub threads complete
th-9 waiting finish
th-0 waiting finish
th-1 waiting finish
th-2 waiting finish
th-4 waiting finish
th-3 waiting finish
th-5 waiting finish
th-7 waiting finish
th-6 waiting finish
th-8 waiting finish

區別

這兩個類,在定義上的區別似乎不大。一個是latch,一個是barrier,都體現了阻塞的意思,並且呼叫await的時候都會阻塞。但是他們阻塞時的執行緒的關係是不一樣的。對於CountDownLatch來說,是一個或多個執行緒在等待一個或多個執行緒,他們的等待關係從巨集觀的角度來看只有一組。而對於CyclicBarrier來說,是所有的執行緒在互相等待,他們的等待關係是交叉的。

另外一個區別是,CyclicBarrier的barrier是可以複用的(呼叫reset方法),但是CountDownLatch的latch只能用一次。這個區別在實際的業務當中會產生一定的影響。


相關文章