CountDownLatch和CyclicBarrier

CountDownLatch类可以实现两种角色的线程等待对方的效果;
CyclicBarrier类可以使同类现成互相等待达到同步的效果;
这两个类可以更加完善的实现现成对象之间的同步性,对线程对象执行的轨迹控制更加方便。

CountDownLatch的使用

它所提供的功能是判断count计数不为0时则当前线程呈wait状态,也就是屏障处等待。
它也是一个同步功能的辅助类,使用效果是给定一个计数,当使用这个CountDownLatch类的线程计数不为0时,则呈wait状态,如果为0时则继续运行。
实现等待与继续运行的效果分别需要使用await()和countDown()方法来进行。调用await()方法时判断计数是否为0,如果不为0则呈等待状态,其他线程可以调用countDown()方法将计数减1,当计数减到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
public class App {
public static void main(String[] args) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
new TestThread(latch).start();
Thread.sleep(2000);
System.out.println("XX");
latch.countDown();
}
}
class TestThread extends Thread {
private CountDownLatch latch;
public TestThread(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println("A");
latch.await();
System.out.println("B");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

运行结果

1
2
3
A
XX
B

方法介绍

1
2
await(long timeout, TimeUnit unit)可设置最大等待时间
getCount()获取当前的计数个数

CyclicBarrier的使用

它不进有CountDownLatch所具有的功能,还可以实现屏障等待的功能,也就是阶段性同步,它在使用上的意义在于可以循环的实现线程要一起做任务的目标,而不是像CountDownLatch一样,仅仅支持一次线程与同步点阻塞的特性。
它也是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点,这些线程必须实时的互相等待,同时它的公共屏障点可以重用,所以类名中包含Cyclic一词。

简单示例

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
public class App {
public static void main(String[] args) throws Exception {
CyclicBarrier cb = new CyclicBarrier(5, new Runnable() {
@Override public void run() {
System.out.println("都到了");
}
});
for (int i = 0; i < 5; i ++) {
new TestThread(cb).start();
}
}
}
class TestThread extends Thread {
private CyclicBarrier cb;
public TestThread(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
try {
Thread.sleep(((int) Math.random()) * 1000);
System.out.println(Thread.currentThread().getName() + "到了,time=" + System.currentTimeMillis());
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}

运行结果

1
2
3
4
5
6
Thread-0到了,time=1525274424626
Thread-4到了,time=1525274424627
Thread-2到了,time=1525274424626
Thread-1到了,time=1525274424627
Thread-3到了,time=1525274424627
都到了

这里演示的时线程个数等于屏障个数的情况,
下面示例线程数大于屏障个数时,分批处理:

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
public class App {
public static void main(String[] args) throws Exception {
CyclicBarrier cb = new CyclicBarrier(2, new Runnable() {
@Override public void run() {
System.out.println("都到了");
}
});
for (int i = 0; i < 4; i ++) {
new TestThread(cb).start();
Thread.sleep(500);
}
}
}
class TestThread extends Thread {
private CyclicBarrier cb;
public TestThread(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "到了,等待凑齐两个,time=" + System.currentTimeMillis());
cb.await();
System.out.println(Thread.currentThread().getName() + "已经凑齐两个,time=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
Thread-0到了,等待凑齐两个,time=1525274766393
Thread-1到了,等待凑齐两个,time=1525274766895
都到了
Thread-1已经凑齐两个,time=1525274766896
Thread-0已经凑齐两个,time=1525274766896
Thread-2到了,等待凑齐两个,time=1525274767398
Thread-3到了,等待凑齐两个,time=1525274767901
都到了
Thread-3已经凑齐两个,time=1525274767901
Thread-2已经凑齐两个,time=1525274767902

方法介绍

1
2
3
4
5
6
7
8
getParties()取得屏障点个数
getNumberWaiting()获取当前有几个线程已经达到屏障点
isBroken()查询此屏障是否处于损坏状态
这里需要注意的是CyclicBarrier对于线程的中断interrupte处理会使用全有或者全无的破坏模型
意思是如果一个线程处于中断或超时提前离开屏障点,其他所有屏障点等待的线程均会跑出BrokenBarrierException或者InterruptedException,并且离开屏障点。
await(long timeout, TimeUnit unit)指定在屏障点的最大等待时间,超时将抛出TimeoutException
reset()重置屏障
处于屏障点等待的线程将抛出BrokenBarrierException