Java多线程:新特性—同步工具

CountDownLatch


CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。

假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

1
2
3
4
5
6
7
8
public CountDownLatch(int count);
构造方法,指定了计数的次数
public void countDown();
当前线程调用此方法,则计数减一
public void await();
调用此方法会一直阻塞当前线程,直到计时器的值为0

例子

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
import java.util.concurrent.CountDownLatch;
/***
* CountDownLatch
*
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch=new CountDownLatch(2);//两个工人的协作
Worker worker1=new Worker("A", latch);
Worker worker2=new Worker("B", latch);
worker1.start();//
worker2.start();//
latch.await();//等待所有工人完成工作
System.out.println("吃饭去...");
}
}
class Worker extends Thread{
String workerName;
CountDownLatch latch;
public Worker(String workerName ,CountDownLatch latch){
this.workerName=workerName;
this.latch=latch;
}
public void run(){
System.out.println("Worker "+workerName+" finish work ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();//工人完成工作,计数器减一
}
}

输出结果:

1
2
3
Worker A finish work
Worker B finish work
吃饭去...

CyclicBarrier


CyclicBarrier和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
44
45
46
47
48
49
50
51
52
53
54
55
56
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* CyclicBarrier
*
*/
public class Test {
public static void main(String[] args) {
//创建障碍器,并设置MainTask为所有定数量的线程都达到障碍点时候所要执行的任务
CyclicBarrier cb = new CyclicBarrier(7, new MainTask());
new SubTask("A", cb).start();
new SubTask("B", cb).start();
new SubTask("C", cb).start();
new SubTask("D", cb).start();
new SubTask("E", cb).start();
new SubTask("F", cb).start();
new SubTask("G", cb).start();
}
}
/**
* 主任务
*/
class MainTask implements Runnable {
public void run() {
System.out.println("主任务执行了...");
}
}
/**
* 子任务
*/
class SubTask extends Thread {
private String name;
private CyclicBarrier cb;
SubTask(String name, CyclicBarrier cb) {
this.name = name;
this.cb = cb;
}
public void run() {
System.out.println("[子任务" + name + "]开始执行了");
for (int i = 0; i < 999999; i++) ; //模拟耗时的任务
System.out.println("[子任务" + name + "]开始执行完成了,并通知障碍器已经完成");
try {
//通知障碍器已经完成
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[子任务B]开始执行了
[子任务D]开始执行了
[子任务A]开始执行了
[子任务E]开始执行了
[子任务C]开始执行了
[子任务A]开始执行完成了,并通知障碍器已经完成
[子任务D]开始执行完成了,并通知障碍器已经完成
[子任务C]开始执行完成了,并通知障碍器已经完成
[子任务F]开始执行了
[子任务B]开始执行完成了,并通知障碍器已经完成
[子任务E]开始执行完成了,并通知障碍器已经完成
[子任务G]开始执行了
[子任务F]开始执行完成了,并通知障碍器已经完成
[子任务G]开始执行完成了,并通知障碍器已经完成
主任务执行了...

总结


CountDownLatch是所有子程序执行完以后,再执行主线程。关注的是主线程。
CyclicBarrier是所有子线程都执行到某一点后,再继续执行。关注的是子线程.