博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CountDownLatch和CyclicBarrier的使用和区别
阅读量:4179 次
发布时间:2019-05-26

本文共 3908 字,大约阅读时间需要 13 分钟。

在并发编程中,总会有各种各样的需求,根据需求去制定解决方案,才能让我们更好的理解,假如我们有以下两个需求:

  1. 有十个线程去执行各自的任务,任务可以分为两个部分,前半部分线程开启就可以执行,后半部分需要需要满足某个条件才能继续往下执行,如果条件暂不满足,那就等待,等到条件满足时就可以开始执行,如果条件满足,就不需要等待,直接往下执行,等十个任务全部做完时才可以在当前线程做其他的事情;

  2. 同样有十个线程去执行各自的任务,任务也是分为两个部分,现在要求等所有前半部分执行完后才能开始执行后半部分。

就这么两个需求,第一个可以用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 block			e.printStackTrace();		}					}	public static class Worker implements Runnable{		CountDownLatch start;		CountDownLatch end;		public Worker(CountDownLatch cLatch, CountDownLatch cLatch2) {			super();			this.start = cLatch;			this.end = cLatch2;		}		@Override		public 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 block				e.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() {            @Override            public void run() {                // TODO Auto-generated method stub                System.out.println("前半部分任务执行完了,这里在执行点自己任务,执行完后就可以执行后半部分任务了!");            }        });        List
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 block e.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; } @Override public void run() { // TODO Auto-generated method stub try { System.out.println("前半部分任务 id = "+id); barrier.await(); // 大家等待最后一个线程到达 System.out.println("后半部分任务 id = "+id); } catch (InterruptedException | BrokenBarrierException e) { // TODO Auto-generated catch block e.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时,也就说没有锁了,这时线程就不在阻塞了,这样下来,上面的那两个需求是不是就特别的简单了,如果说是用我们自己定义的锁,那实现起来可就没有这么简单了,这两个例子算是比较简单了,如果你还想更加深入的了解,可以结合下源码,源码不多,方法数也不多,结合这两个例子是很容易理解的。

 

转载地址:http://bkhai.baihongyu.com/

你可能感兴趣的文章
第一篇: 服务的注册与发现Eureka(Greenwich版)
查看>>
第二篇: 服务消费者(rest+ribbon)(Greenwich版本)
查看>>
第三篇: 服务消费者(Feign)(Greenwich版本)
查看>>
获取客户的真实IP地址
查看>>
第四篇: 熔断器(Ribbon+Feign)(Greenwich版本)
查看>>
Linux的常用命令(一)
查看>>
Linux的常用命令(二)
查看>>
第六篇: 分布式配置中心(Greenwich版本)
查看>>
SpringBoot | 配置logback-spring.xml
查看>>
SpringBoot | 第一章:构建第一个SpringBoot工程
查看>>
SpringBoot | 第二章:配置多环境以及上传文件
查看>>
Spring Data JPA |自定义非实体类的映射
查看>>
SpringBoot | 常用注解记录
查看>>
JavaBean对象转换EntityUtils工具类
查看>>
Maven常用命令
查看>>
SpringBoot | 运行报错,无法加载oracle连接驱动
查看>>
为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作
查看>>
AWS EC2如何从普通用户切换为root用户
查看>>
click方法不生效的
查看>>
mysql排行榜并列与不并列
查看>>