开篇即说结论,如果搞不清楚两者区别,那就无脑用 CountDownLatch,问题也不大(因为我也不是太懂)。
CountDownLatch
模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。
* <a href="https://www.cnblogs.com/liuchao102/p/4383894.html">...</a>
*
* @author liuchao
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//10名运动员
final CountDownLatch count = new CountDownLatch(10);
//java的线程池
final ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int index = 1; index <= 10; index++) {
final int number = index;
executorService.submit(() -> {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println(number + ": arrived");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
count.countDown();// 运动员到达终点,count数减一
}
});
}
System.out.println("Game Started");
//等待count数变为0,否则会一直处于等待状态,游戏就没法结束了
count.await();
System.out.println("Game Over");
//关掉线程池
executorService.shutdown();
}
}
运行结果:
CyclicBarrier
继续还是这10个人,这次没裁判。规定10个人只要都跑到终点了,大家可以喝啤酒。但是,只要有一个人没到终点,就不能喝。 这里也没有要求大家要同时起跑(当然也可以,加 latch)
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
final int count = 5;
final CyclicBarrier barrier = new CyclicBarrier(count, () -> System.out.println("drink beer!"));
// they do not have to start at the same time...
for (int i = 0; i < count; i++)
new Thread(new Worker(i, barrier)).start();
}
}
class Worker implements Runnable {
final int id;
final CyclicBarrier barrier;
public Worker(final int id, final CyclicBarrier barrier) {
this.id = id;
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println(id + "starts to run !");
Thread.sleep((long) (Math.random() * 10000));
System.out.println(id + "arrived !");
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
运行结果:
总结
- CountDownLatch 强调一个线程等多个线程完成某件事情。CyclicBarrier 是多个线程互等,等大家都完成。
- CountDownLatch 很明显是可以不限制等待线程的数量,而会限制 countDown的操作数;CyclicBarrier 会限制等待线程的数量
- 写法当然不一样,看看下面的伪代码
class CountDownLatch{
CountDownLatch cd = new CountDownLatch(5);
main(){
启动5个线程。。。
cd.await(); //让当前线程休眠,
执行代码... (该代码不会立即执行,等待5个线程执行完成后,当前线程会被唤醒,继续执行后面的代码)
}
class Th extends Thread{
public void run() {
执行代码...
cd.countDown(); //报告CountDownLatch 当前线程已经到位
执行代码...(该代码不会因为countDown()而阻塞,countDown()没有阻塞效果)
}
}
}
// 没有指定 构造线程的barrier伪代码
class CyclicBarrier{
CyclicBarrier cd = new CyclicBarrier(5);
main(){
启动5个线程。。。
执行代码...; //该代码会被立即执行
}
class Th extends Thread{
public void run() {
执行代码...
cb.await(); //barrier参与量+1,并且当前线程休眠
执行代码...(该代码不会立即执行,只有等待指定的5个线程集合了后,该代码才会继续执行)
}
}
}
// 指定 构造线程的barrier伪代码
class CyclicBarrier{
CyclicBarrier cd = new CyclicBarrier(5,runable);
main(){
启动5个线程。。。
执行代码...; //该代码会被立即执行
}
class Th extends Thread{
public void run() {
执行代码...
cb.await(); //barrier参与量+1,并且当前线程休眠
执行代码...(该代码不会立即执行,只有等待指定的5个线程集合了后,会启动构造传入的runable线程代码,该线程结束后(所有等待的线程才能被唤醒),该代码才会继续执行)
}
}
}
参见
- CountDownLatch与CyclicBarrier 通熟易懂
- CyclicBarrier正确的使用方法和错误的使用方法
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
- 多线程并发之CyclicBarrier(栅栏)使用详解
- 关于CyclicBarrier与CountDownLatch的源码比较-CountDownLatch 使用场景
- [Java7并发编程实战手册]3.5 在集合点的同步CyclicBarrier循环barrier
- Java CyclicBarrier vs CountDownLatch