本文主要是介绍CountDownLatch和CyclicBarrier的使用和区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在并发编程中,总会有各种各样的需求,根据需求去制定解决方案,才能让我们更好的理解,假如我们有以下两个需求:
-
有十个线程去执行各自的任务,任务可以分为两个部分,前半部分线程开启就可以执行,后半部分需要需要满足某个条件才能继续往下执行,如果条件暂不满足,那就等待,等到条件满足时就可以开始执行,如果条件满足,就不需要等待,直接往下执行,等十个任务全部做完时才可以在当前线程做其他的事情;
-
同样有十个线程去执行各自的任务,任务也是分为两个部分,现在要求等所有前半部分执行完后才能开始执行后半部分。
就这么两个需求,第一个可以用CountDownLatch来解决,第二个可以用CyclicBarrier来解决,下面看代码:
-
CountDownLatch
public static void main(String[] args) {CountDownLatch start = new CountDownLatch(1);CountDownLatch end = new CountDownLatch(5);for (int i = 0; i < 5; i++) {new Thread(new Worker(start, end)).start();}try {//这里就是你所需要添加条件的地方,条件满足就执行下面的方法,这是阻塞的线程既可以再次开始了if(true) {start.countDown();}end.await();System.out.println("所有任务做完了,接下来你可以干自己想干的了");} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static class Worker implements Runnable{CountDownLatch start;CountDownLatch end;public Worker(CountDownLatch cLatch, CountDownLatch cLatch2) {super();this.start = cLatch;this.end = cLatch2;}@Overridepublic void run() {try {System.out.println("执行你任务的前半部分 = "+Thread.currentThread().getName());start.await();System.out.println("执行你任务的后半部分 = "+Thread.currentThread().getName());end.countDown();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
输出结果:
执行你任务的前半部分 = Thread-0
执行你任务的前半部分 = Thread-2
执行你任务的前半部分 = Thread-1
执行你任务的前半部分 = Thread-3
执行你任务的后半部分 = Thread-1
执行你任务的后半部分 = Thread-3
执行你任务的前半部分 = Thread-4
执行你任务的后半部分 = Thread-4
执行你任务的后半部分 = Thread-2
执行你任务的后半部分 = Thread-0
所有任务做完了,接下来你可以干自己想干的了
CountDownLatch的构造函数需要传一个数,我们假定是count,这个数意味着可以调用countDown()多少次,我们可以这样去理解,没调用一次countDown(),count就减1,只要count不为0,那么调用它的await()方法时,就会阻塞当前线程,直到count减为0时,之前调用到await()方法的地方就不在阻塞了,直白一点就是,只要count为0,不管什么时候调用到的await()方法都不在阻塞线程了,反之则一直阻塞线程。
-
CyclicBarrier
public static void main(String[] args) {int num = 10;CyclicBarrier barrier = new CyclicBarrier(num, new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println("前半部分任务执行完了,这里在执行点自己任务,执行完后就可以执行后半部分任务了!");}});List<Thread> list = new ArrayList<>();for (int i = 1; i <= num; i++) {Thread thread = new Thread(new CyclicBarrierWorker(i, barrier));list.add(thread);thread.start(); }//这里是为了让子线程的任务先执行完在执行主线程的任务for (Thread thread : list) {try {thread.join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println("所有的后半部分任务都执行完了,你可以做自己的事了");}public static class CyclicBarrierWorker implements Runnable {private int id;private CyclicBarrier barrier;public CyclicBarrierWorker(int id, final CyclicBarrier barrier) {this.id = id;this.barrier = barrier;}@Overridepublic void run() {// TODO Auto-generated method stubtry {System.out.println("前半部分任务 id = "+id);barrier.await(); // 大家等待最后一个线程到达System.out.println("后半部分任务 id = "+id);} catch (InterruptedException | BrokenBarrierException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
输出结果:
前半部分任务 id = 1
前半部分任务 id = 3
前半部分任务 id = 2
前半部分任务 id = 4
前半部分任务 id = 9
前半部分任务 id = 5
前半部分任务 id = 8
前半部分任务 id = 7
前半部分任务 id = 10
前半部分任务 id = 6
前半部分任务执行完了,这里在执行点自己任务,执行完后就可以执行后半部分任务了!
后半部分任务 id = 6
后半部分任务 id = 4
后半部分任务 id = 9
后半部分任务 id = 3
后半部分任务 id = 2
后半部分任务 id = 1
后半部分任务 id = 10
后半部分任务 id = 7
后半部分任务 id = 8
后半部分任务 id = 5
所有的后半部分任务都执行完了,你可以做自己的事了
CyclicBarrier的构造函数也是需要传入一个数,我们取为count,这也就是说总共有count个栅栏锁,每调用一次它的await()方法时,栅栏锁就少一个,但这时是会阻塞线程的,当栅栏锁减为0时,也就说没有锁了,这时线程就不在阻塞了,这样下来,上面的那两个需求是不是就特别的简单了,如果说是用我们自己定义的锁,那实现起来可就没有这么简单了,这两个例子算是比较简单了,如果你还想更加深入的了解,可以结合下源码,源码不多,方法数也不多,结合这两个例子是很容易理解的。
这篇关于CountDownLatch和CyclicBarrier的使用和区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!