在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;
}
@Override
public 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

通过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;
}
@Override
public 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

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

CyclicBarrie还有一个构造方法是

public CyclicBarrier(int parties, Runnable barrierAction) {}

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

CyclicBarrier cyclicBarrier=new CyclicBarrier(N, new Runnable() {
@Override
public 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

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

CountDownLatch/CyclicBarrie用法记录的更多相关文章

  1. pt-kill 用法记录

    pt-kill 用法记录 # 参考资料Percona-Toolkit系列之pt-kill杀会话利器http://www.fordba.com/percona-toolkit-pt-kill.html ...

  2. Java并发编程之CountDownLatch的用法

    一.含义 CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能.CountDownLatch是一个同步的辅助类,它可以允许一个或多个线程等待, ...

  3. CURL 用法记录

    CURL 用法记录 在工作中经常需要用到curl 命令,记录一下常用的场景 Send a POST Request with JSON Data curl -d '{"login" ...

  4. python 一些函数和类用法记录

    这一篇主要用来记录在学习过程中遇到的一些觉得有意思的函数或者类的用法,有一些用法感觉很炫酷. 1.collections.defaultdict from collections import def ...

  5. 多线程之CountDownLatch的用法及原理笔记

    前言-CountDownLatch是什么? CountDownLatch是具有synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其他线程的一系列操作完成. CountDownL ...

  6. jqueryui / accordion的用法记录

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

  7. Java Concurrency - 浅析 CountDownLatch 的用法

    The Java concurrency API provides a class that allows one or more threads to wait until a set of ope ...

  8. asp.net 第三方UI控件 Telerik KendoUI 之 TreeVIew 的用法记录

    一.前台显示 备注:一次性取出所有节点 function loadTreeData() { $.ajax({ type: 'POST', url: '@(Html.UrlHref("Scri ...

  9. jquery图片延迟加载 及 serializeArray、serialize用法记录

    1.使用jquery实现 图片延迟加载 由于用户访问页面需要加载很多的图片,延迟加载技术在电子商务网站领域越来越普及,淘宝商城,京东商城,凡客等访问量巨大的电子商务站点为了增加用户用户体验,访问速度以 ...

随机推荐

  1. Github常用用法

    基本要求已安装Github客户端 一.新建项目 1.首先创建代码仓库,复制仓库地址: 2.本地带上传文件目录,鼠标右键,打开git -> Git Bash Here -> git init ...

  2. java39

    String a= "hello.a.java;b.java;hello.java;hello.toha;"; //将每个分号的内容取出来 String[] res=a.split ...

  3. 防止用户重发发生ajax请求

    1.前端限制 点击提交后,将该元素禁用,等待请求结束后再次释放(解除禁用). 可以使用ajax中的 success 请求成功后的回调函数进行按钮释放. 2.防抖动 暴力连续点击按钮,可以通过闭包里的  ...

  4. scrum与第一次teamwork

    一.关于Scrum Scrum是什么?是迭代式增量软件开发过程,通常用于敏捷软件开发,Scrum是一种偏重于过程的敏捷开发的具体方式.Scrum的英文意思是橄榄球运动的一个专业术语,表示“争球”的动作 ...

  5. PHP获取时间戳和微秒数以及生成唯一ID

    microtime函数 描述:返回当前Unix时间戳和微秒数 语法:mixed microtime( [ bool $get_as_float ] ) //直接输出 echo microtime(); ...

  6. 移动端canvas文字图片合成并生成图片(canvas宽度自适应移动端屏幕)

    这是我之前做的一个关于文字图片合成的代码,供大家参考,不足支出还望体谅:具体的注释在代码里都有,有什么不懂了可以留言互相交流.<!DOCTYPE html> <html lang=& ...

  7. 第52章:Java操作MongoDB-[Mongo-Java-3.x]

    ①范例:连接数据库 package cn.mldn; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import ...

  8. [转]数据库中间件 MyCAT源码分析——跨库两表Join

    1. 概述 2. 主流程 3. ShareJoin 3.1 JoinParser 3.2 ShareJoin.processSQL(...) 3.3 BatchSQLJob 3.4 ShareDBJo ...

  9. 取消Debian屏保及显示器休眠

    在产品展示场合,屏保及休眠会带来不好的体验,很多时候需要关闭掉. dpms显示器休眠设置: 开启:$ sudo xset dpms 1 1 2取消:$ sudo xset -dpms xset设置屏保 ...

  10. [solution]JZOJ-5838 旅游路线

    [solution] JZOJ-5838 旅游路线 Time Limits 1000ms,Memory Limits 128MB 题面 Description GZOI队员们到X镇游玩.X镇是一个很特 ...