CyclicBarrier源码阅读
一种允许多个线程全部等待彼此都到达某个屏障的同步机制
使用
多个线程并发执行同一个CyclicBarrier实例的await方法时,每个线程执行这个方法后,都会被暂停,只有当最后一个线程执行完await方法时,它自身不会暂停,且会唤醒所有等待线程。因为CyclicBarrier内部维护了一个显式锁,它可以识别最后一个执行线程。
CyclicBarrier内部维护一个trip变量来实现等待/通知,所有除最后一个线程的保护条件都是“当前generation中,仍未执行的线程数大于0”,这个仍未执行的线程数默认为总参与线程的数量,await方法每执行一次,这个数量递减1.
使用场景
模拟高并发,或放在一个循环中,使得当前迭代操作的结果作为下一个迭代的基础输入;不然可以直接使用Thread.join()或CoutDwonLatch
问题:
1.可以设置多个屏障吗:可以,以generation区分(cyclic)
示例:
class MyThread extends Thread {
private CyclicBarrier cb;
public MyThread(String name, CyclicBarrier cb) {
super(name);
this.cb = cb;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " going to await");
try {
cb.await();
System.out.println(Thread.currentThread().getName() + " continue");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
CyclicBarrier cb = new CyclicBarrier(3, new Thread("barrierAction") {
public void run() {
System.out.println(Thread.currentThread().getName() + " barrier action");
}
});
MyThread t1 = new MyThread("t1", cb);
MyThread t2 = new MyThread("t2", cb);
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName() + " going to await");
cb.await();
System.out.println(Thread.currentThread().getName() + " continue");
}
}
源码阅读
public class CyclicBarrier {
//屏障的每一次使用都代表一代示例
private static class Generation {
//屏障是否被破坏
boolean broken = false;
}
//防止线程跳过屏障的锁 默认非公平锁
private final ReentrantLock lock = new ReentrantLock();
//等待线程跳过屏障的条件
private final Condition trip = lock.newCondition();
//参与线程数量
private final int parties;
//跳过屏障后执行的指令
private final Runnable barrierCommand;
//当前阶段????
private Generation generation = new Generation();
//每一阶段仍在等待的线程
private int count;
//barrierAction:跳过屏障后执行的指令
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
//线程到达屏障 则等待:外部等待调用方法
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
/**
* 执行过程:
* 加锁 -> 判断屏障是否被破坏(抛异常) -> 判断线程是否被中断(毁坏屏障,抛异常) ->
* 判断是否仍有等待线程(N -> 执行屏障命令; Y -> 进入屏障等待) -> 解锁
*/
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//锁住当前线程
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
//线程被中断 唤醒所有等待线程 损坏当前屏障 抛出异常
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//获得并减少正在等待进入屏障的线程个数
int index = --count;
if (index == 0) { // 全部进入屏障了 执行后续命令
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//唤醒所有等待线程 进入下一代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 自旋直到跳过屏障/中断/超时 停止
for (;;) {
try {
//没有设置时间 直接等待
if (!timed)
//线程等待
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
//不是当前代的 返回索引
if (g != generation)
return index;
//设置了等待时间且时间小于等于0
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
//条件:所有线程处于等待状态
private void nextGeneration() {
// 唤醒所有线程
trip.signalAll();
// 恢复等待线程数
count = parties;
generation = new Generation();
}
}
CyclicBarrier源码阅读的更多相关文章
- 《java.util.concurrent 包源码阅读》 结束语
<java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...
- 并发工具CyclicBarrier源码分析及应用
本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天呢!灯塔君跟大家讲: 并发工具CyclicBarrier源码分析及应用 一.CyclicBarrier简介 1.简介 CyclicBarri ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 【原】FMDB源码阅读(一)
[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...
- 【原】AFNetworking源码阅读(六)
[原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...
- 【原】AFNetworking源码阅读(五)
[原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...
- 【原】AFNetworking源码阅读(四)
[原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...
- 【原】AFNetworking源码阅读(三)
[原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...
随机推荐
- 树莓派USB存储设备自动挂载并通过脚本实现自动拷贝,自动播放视频,脚本自动升级等功能
需求:首先需要树莓派自动挂载USB设备,然后扫描USB指定目录下文件,将相关文件拷贝至树莓派指定目录,然后通过omxplayer循环播放新拷贝文件视频 1. 树莓派实现USB存储设备自动挂载 树莓派U ...
- laravel 中使用es 流程总结
1. query_string 2.mutil_match 3.match 4.should.must.bool 5.analysiz
- C# 枚举转集合
记录一下,方便自己下次使用. public class EnumHelper { /// <summary> /// 将枚举转为集合 /// </summary> /// &l ...
- 01 Redis基础
NoSQL 学名(not only sql) 特点: 存储结构与mysql这一种关系型数据库完全不同,nosql存储的是KV形式 nosql有很多产品,都有自己的api和语法,以及业务场景 产品种类: ...
- 淘宝flexible.js的使用
首先大家最关注的怎么使用,原理不原理是后面的事 比如设计稿量来的宽度是100px 那么我们布局的时候,就这么写{width:1.3333rem},1.3333rem是由100/75算出来的,以此类推2 ...
- 基于linux与busybox的reboot命令流程分析
http://www.xuebuyuan.com/736763.html 基于Linux与Busybox的Reboot命令流程分析 ********************************** ...
- 最完美ThinkPHP Nginx 配置文件
一个配置文件,完美支持普通,兼容,pathinfo,rewrite4种url模式,别怪我没提醒你收藏哦. 常见的静态文件404时也不会再去跑一遍fastcgi浪费资源. server { listen ...
- awk处理实记
经grep日志后得到的数据格式如下: } . [debug][-- ::] SendDataStyled:{ , "innings" : "6189269620_0007 ...
- MSSQL日期分组排序
等于今天日期的排上面,大于今天的排中间,小于今天的排下面,带分页.
- python 教程(一)
必须感慨一下,整了一周多的时间才基本理通顺.自己老是不能静心,方法也不对所以走了很多弯路. 1.建议:一定要先去看官方文档. 下面我们来看一周的成果吧: windows下如何下载并安装Python 3 ...