CountDownLatch和CyclicBarrier区别
你真的理解CountDownLatch与CyclicBarrier使用场景吗?
参考URL: https://www.cnblogs.com/shihaiming/p/11407417.html
CountdownLatch和CyclicBarrier的区别使用场景与具体实现
参考URL: https://zhuanlan.zhihu.com/p/139020914
CountDownLatch和CyclicBarrier使用上的区别
参考URL: https://www.cnblogs.com/geekdc/p/11541266.html
CountDownLatch和CyclicBarrier都是java.util.concurrent包下面的多线程工具类。CountdownLatch和CyclicBarrier都属于线程同步的工具。
CountDown表示减法计数,Latch表示门闩的意思,计数为0的时候就可以打开门闩了。
Cyclic Barrier表示循环的障碍物。
翻译:
CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。
他们都是:Synchronization aid,我把它翻译成同步辅助器,既然是辅助工具。
CountDownLatch: 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。
CyclicBrrier: N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
总结:
- CountDownLatch和CyclicBarrier都是用作多线程同步,CountDownLatch基于AQS,CyclicBarrier基于ReentrantLock
- CyclicBarrier支持复用和barrierCommand,但是CountDownLatch不支持。
- CyclicBarrier会阻塞线程,在最后一个任务执行线程完成之前,其余线程都必须等待,而线程在调用CountDownLatch的countDown方法之后就会结束。
使用场景上对比以及demo
CyclicBarrier和CountDownLatch使用上的区别
参考URL: https://blog.youkuaiyun.com/xqnode/article/details/81867857
CountdownLatch 使用场景:
顾名思义CountdownLatch可以当做一个计数器来使用,比如某线程需要等待其他几个线程都执行过某个时间节点后才能继续执行 。
CyclicBarrier 使用场景:
所有线程在其他线程没有准备好之前都在被阻塞中,等到所有线程都准备好了才继续执行。
在学习了CyclicBarrier之后发现,CyclicBarrier也可以实现跟CountDownLatch类似的功能,只需要在它的parties中多设置一个数,将主线程加入等待队列就可以了。
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
int size = 3;
// 设置参数时,线程实际执行数size+1,将main线程也加到等待队列中
CyclicBarrier cyclicBarrier = new CyclicBarrier(size + 1);
for (int i = 0; i < size; i++) {
int index = i;
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(index);
System.out.println("第" + index + "位运动员准备好了");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {
//主线程也加入等待
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(size + "位运动员都准备好了,可以起跑!");
}
**需求:**有三位运动员,他们一起参加万米赛跑,但是他们准备的时间不同,要等他们都准备好了再开始一起跑。
使用CyclicBarrier 实现:
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
parties 是参与线程的个数
第二个构造方法有一个 Runnable 参数,这个参数的意思是最后一个到达线程要做的任务。
import java.util.concurrent.*;
public class RunTest {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
int size = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(size, () -> {
System.out.println(size + "位运动员都准备好了,可以起跑!");
pool.shutdownNow();
});
for (int i = 0; i < size; i++) {
int index = i;
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(index);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + index + "位运动员准备好了");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
}
}
我们在创建CyclicBarrier对象时传入了一个方法,当调用CyclicBarrier的await方法后,当前线程会被阻塞等到所有线程都调用了await方法后 调用传入CyclicBarrier的方法,然后让所有的被阻塞的线程一起运行。
可以看到,三位运动员准备的时间分别是1s,2s,3s。系统等到他们都准备好了,再发出起跑的信号。在这里CyclicBarrier 做法是在自己的构造器中new了一个runnable,等待其他线程都执行完,再执行此runnable中的代码。
我们再看看CountDownLatch怎么实现:
import java.util.concurrent.*;
public class RunTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(3);
int size = 3;
for (int i = 0; i < size; i++) {
int index = i;
pool.submit(() -> {
try {
TimeUnit.SECONDS.sleep(index);
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + index + "位运动员准备好了");
});
}
countDownLatch.await();
System.out.println(size + "位运动员都准备好了,可以起跑!");
}
}
countDownLatch是采取阻塞主线程的方法实现了线程的统一。他内部有一个计数器,我们在执行完一次线程任务的时候需要手动的减一个数,在主线程中使用 **countDownLatch.await()**监控计数器的状态,知道计数器计到0为止,再执行主线程的代码。