在jdk1.5中,java提供了很多工具类帮助我们进行并发编程,其中就有CountDownLatch和CyclicBarrie

1.CountDownLatch的用法


CountDownLatch 位于 java.util.concurrent 包下,其中最主要的方法就是 两个await方法了, 当我们调用await方法时,当前线程会被挂起,直到count的值为零才继续执行

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public boolean await(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}

我们可以写一个小demo看看CountDownLatch的用法

public class CountDownLatchDemo {static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-DD hh:mm:ss");static  CountDownLatch countDownLatch=new CountDownLatch(2);public static void main(String[] args) throws InterruptedException {System.out.println(" 当前线程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date())+"" +"当前计数器:"+countDownLatch.getCount());new Thread(new Task(3000)).start();new Thread(new Task(5000)).start();//等待计数器的值为0
        countDownLatch.await();System.out.println(" 当前线程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date())+"" +"当前计数器:"+countDownLatch.getCount());}static class Task implements Runnable{private Integer time;public Task(Integer time) {this.time=time;}@Overridepublic void run() {System.out.println(" 当前线程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date())+"" +"当前计数器:"+countDownLatch.getCount());try {//模拟业务执行,休眠一段时间
                Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}//执行完毕之后把计数器的值减一
            countDownLatch.countDown();System.out.println(" 当前线    程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date())+"" +"当前计数器:"+countDownLatch.getCount());}}
}

执行的结果是

当前线程:main 当前时间:2018-01-22 10:22:10当前计数器:2当前线程:Thread-0 当前时间:2018-01-22 10:22:10当前计数器:2当前线程:Thread-1 当前时间:2018-01-22 10:22:10当前计数器:2当前线程:Thread-0 当前时间:2018-01-22 10:22:13当前计数器:1当前线程:Thread-1 当前时间:2018-01-22 10:22:15当前计数器:0当前线程:main 当前时间:2018-01-22 10:22:15当前计数器:0

View Code

通过CountDownLatch 我们可以用于 某个线程A等到其他线程执行完毕后,它才执行

2.CyclicBarrier的用法


CyclicBarrier在功能上可能和CountDownLatch有点类似,都有线程挂起的功能,不过CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

我可以看一个小例子

public class CyclicBarrierDemo2 {public static void main(String[] args) throws InterruptedException {int N=3;CyclicBarrier cyclicBarrier=new CyclicBarrier(N);for(int i=0;i<N;i++){Thread.sleep(i*1000);new Thread(new Barrier(cyclicBarrier)).start();}}
}class Barrier implements Runnable{SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-DD hh:mm:ss");CyclicBarrier cyclicBarrier;public Barrier(CyclicBarrier cyclicBarrier){this.cyclicBarrier=cyclicBarrier;}@Overridepublic void run() {try {System.out.println(" 当前线程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date()));//模拟业务Thread.sleep(3000);//等待其他线程到达await状态
            cyclicBarrier.await();System.out.println(" 【阻塞完成】当前线程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}
}

以上代码运行结果是

当前线程:Thread-0 当前时间:2018-01-22 10:56:51当前线程:Thread-1 当前时间:2018-01-22 10:56:52当前线程:Thread-2 当前时间:2018-01-22 10:56:54【阻塞完成】当前线程:Thread-2 当前时间:2018-01-22 10:56:57【阻塞完成】当前线程:Thread-0 当前时间:2018-01-22 10:56:57【阻塞完成】当前线程:Thread-1 当前时间:2018-01-22 10:56:57

View Code

可以看到3个线程在运行到 cyclicBarrier.await() 时,线程会处于barrier状态,同时会检查其他线程是否也处于barrier 状态了,如果大家都处于barrier状态了,所有线程一起往下接着运行

CyclicBarrie还有一个构造方法是

public CyclicBarrier(int parties, Runnable barrierAction) {}

作用是当子线程都处于barrier状态了,会任意挑选一个线程来执行这个Runnable线程,我们可以把以上案例改造下

CyclicBarrier cyclicBarrier=new CyclicBarrier(N, new Runnable() {@Overridepublic void run() {System.out.println("【主线程执行】当前线程:"+Thread.currentThread().getName()+" " +"当前时间:"+simpleDateFormat.format(new Date()));}});

执行的结果就是

当前线程:Thread-0 当前时间:2018-01-22 11:01:05当前线程:Thread-1 当前时间:2018-01-22 11:01:06当前线程:Thread-2 当前时间:2018-01-22 11:01:08
【主线程执行】当前线程:Thread-2 当前时间:2018-01-22 11:01:11【阻塞完成】当前线程:Thread-2 当前时间:2018-01-22 11:01:11【阻塞完成】当前线程:Thread-0 当前时间:2018-01-22 11:01:11【阻塞完成】当前线程:Thread-1 当前时间:2018-01-22 11:01:11

View Code


CountDownLatch和CyclicBarrie还有个不同是CyclicBarrie可以重用而CountDownLatch却不可以。

这个其实也很容易理解,CountDownLatch需要不停的减到零才阻塞,这就相当于是个计数器

而CyclicBarrie 却像一个开关,每次都处于barrier 开关打开。

CountDownLatch的内部实现


先从构造函数开始,CountDownLatch的内部也有一个Sync内部类

Sync 继承了AQS,可见AQS真是并发包中大爸爸,本次为什么想要了解CountDownLatch的内部实现呢,其实也是想借此了解下AQS中共享锁的实现,顺便再次膜拜下Doug Lea

先从state开始,可以看到和独占锁一样,AQS同样有个state的变量用于控制node节点能否获取到锁。

再看下await方法

再看下acquireSharedInterruptibly方法

1.前两行会检查下线程是否被打断

2.尝试着获取共享锁,小于0,表示获取失败,如果失败会将当前线程放在队列中

这一行就是CountDownLatch的核心,如果CountDownLatch的值大于0,线程会一直阻塞,因为线程在获取共享锁的时候必然会失败。

doAcquireSharedInterruptibly 这个方法应该是把线程放到队列里了

一直到这,我还没找到共享锁的核心在哪呢,再看下 setHeadAndPropagate 内部

当线程被唤醒后,会重新尝试获取共享锁,而对于CountDownLatch线程获取共享锁判断依据是state是否为0,而这个时候显然state已经变成了0,因此可以顺利获取共享锁并且依次唤醒AQS队里中后面的节点及对应的线程。

最后总结下

在获取时,维护了一个sync队列,每个节点都是一个线程在进行自旋,而依据就是自己是否是首节点的后继并且能够获取资源;
在释放时,仅仅需要将资源还回去,然后通知一下后继节点并将其唤醒。
这里需要注意,队列的维护(首节点的更换)是依靠消费者(获取时)来完成的,也就是说在满足了自旋退出的条件时的一刻,这个节点就会被设置成为首节点。

1. CountDownLatch内部有count计数器
2.count的计数器为0,线程才能获取锁
3.否则的的话就会进入到队列中去
4.一旦减到0,队列的第一个节点就会尝试获取锁
5.获取成功后会唤醒下一个节点,让他去尝试获取锁,以此不断的往下延续


参考:http://www.infoq.com/cn/articles/java8-abstractqueuedsynchronizer

转载于:https://www.cnblogs.com/xmzJava/p/8328127.html

CountDownLatch/CyclicBarrie用法记录相关推荐

  1. 小白练习cocos creator——property用法记录

    property用法记录 参考文档:官方文档-属性检查器 在记录property用法之前,先说下从官方文档中摘抄的笔记:用property修饰的属性是组件脚本中声明的公开的并可被序列化存储在场景和动画 ...

  2. 【二开】Jeecgboot Online表单js增强用法记录

    [二开]Jeecgboot Online表单js增强用法记录 表单js增强 loaded(){this.$nextTick(()=>{//获取表单maplet rows = this.getFi ...

  3. set classpath用法记录

    set classpath用法记录 1.set classpath(查看classpath路径) 2.set classpath= 3.set classpath=d:\test (test后不加分号 ...

  4. mysql查询去重第一条_Mysql用法记录 - Ashley-OSCHINA的个人空间 - OSCHINA - 中文开源技术交流社区...

    1.查询某个字段,在数据库不重复的条数(去重查询) select count(distinct item_uid) from supply_order; 查询 表 supply_order 中 ite ...

  5. Python读取显示raw图片+numpy基本用法记录

    这次任务是拿到了几张raw图片,没其他头文件或信息,需要对raw图片使用python打开并显示 (作为了一个初学者,整天搞jpg.png,突然来了个raw,表示很头大) 1.获取raw图片shape信 ...

  6. CyclicBarrier和CountDownLatch的用法与区别

    前言 CyclicBarrier和CountDownLatch这两个工具都是在java.util.concurrent包下,并且平时很多场景都会使用到. 本文将会对两者进行分析,记录他们的用法和区别. ...

  7. vue 2.6 插槽v-slot用法记录

    v-slot用法简记 用法示例 匿名插槽与具名插槽 插槽作用域 组件使用插槽动态命名 总结 用法示例 vue2.6统一了插槽的语法v-slot 匿名插槽与具名插槽 在其他组件中使用child组件 &l ...

  8. jqueryui / accordion的用法记录

    jqueryui 的 widget 中包含了基本上我们都需要的ui组件, 除了那个unslider. 参考地址是: www.jqueryui.com. 要能够看懂/并使用/ 完全掌握的话, 就要使用其 ...

  9. C# Winform ToolStripContainer ToolStrip相关用法记录

    用过VS的都知道,顶部有很多工具条,可以显示隐藏,也可以拖来拖去.这个在winform里面就是用ToolStripContainer实现的.如何使用,网上帖子多得是,这里记录一下我被坑的地方. 1.T ...

最新文章

  1. ogg oracle to mysql_ogg oracle to mysql
  2. 虚拟机的性能监控与故障处理——jps,jstat,jinfo,jmap,jhat,jstack
  3. 简单句(Simple sentences)-one
  4. matlab consumption,Lesage matlab 空间
  5. 自己研发的系统给rtx发消息
  6. 合作博弈网页小游戏-Js源码
  7. Netty系列三、Netty实战篇
  8. 超全汇总 | 基于Camera的3D目标检测算法综述!(单目/双目/伪激光雷达)
  9. 把数字翻译成字符串——python
  10. 记录通过的阿里云认证
  11. Window / Mac 系统 nvm 安装使用指南
  12. itext 5.3.0实现对pdf文件添加(文字和图片)水印
  13. 四天搞懂生成对抗网络(一)——通俗理解经典GAN
  14. [Linux From Scratch 作一个自己的Linux操作系统发行版本]一、环境准备
  15. 与国同庆--单片机小白自制蓝牙避障小车
  16. DSL Forum首席运营官Robin Mersh在中国的演讲
  17. ajax异步执行调用什么机制,大白话讲解JavaScript 执行机制,一看就懂
  18. spark按照key分区:partitionBy
  19. 从零开始学USB(五、USB的电器特性)
  20. 量化交易之回测篇 - 重写vnpy自带的双均线策略

热门文章

  1. android Content provider 组件
  2. 文末送书 | 阿里资深员工撰写:深度实践OCR
  3. ICCV NAS Workshop 最佳论文提名:通过层级掩码实现高效神经网络架构搜索
  4. Reddit热议:为什么PyTorch比TensorFlow更快?
  5. mysql groupby 拼接_mysql分组并多行拼接--group_concat和groupby的使用
  6. Python学得好,升职加薪下班早
  7. anchor free 目标检测_《目标检测》系列之二:目标检测中的Anchor机制回顾
  8. 经验 | 清华大学计算机系教授~浅谈研究生学位论文选题方法
  9. 【Python】Python实战从入门到精通之五 -- 教你使用文件写入
  10. 图像学习-HOG特征