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 ...
随机推荐
- O008、LVM类型的Storage Pool
参考https://www.cnblogs.com/CloudMan6/p/5277927.html LVM类型的Storage Pool 不仅一个文件可以分配给客户机作为虚拟磁盘,宿主机上 ...
- java实现spark常用算子之filter
import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaRDD;import org.apache.spark.a ...
- debezium关于cdc的使用(上)
博文原址:debezium关于cdc的使用(上) 简介 debezium是一个为了捕获数据变更(cdc)的开源的分布式平台.启动并指向数据库,当其他应用对此数据库执行inserts.updates.d ...
- 用java语言(文件和文件流知识点)实现图片的拷贝,从d盘拷贝到e盘
/** * 实现图片的拷贝\ * 注意:用的是文件字节流 */ package com.test4; import java.io.*; public class Demo12_4 { /** * @ ...
- 解决 webpack 打包文件体积过大
webpack 把我们所有的文件都打包成一个 JS 文件,这样即使你是小项目,打包后的文件也会非常大.下面就来讲下如何从多个方面进行优化. 去除不必要的插件 刚开始用 webpack 的时候,开发环境 ...
- 使用docker构建supervisor全步骤
1.使用docker build 命令基于Dockerfile文件进行构建supervisor镜像,命令:docker build -t supervisor镜像名 Dockerfile文件放置的位置 ...
- SQL Server里面如何导出包含数据的SQL脚本
通常情况下,SQL Server里面的生成SQL脚本,只会包含数据库及表的字段结构,而不会包含表的数据,也就是SQL脚本里面只有Create database,Create table 这样的语句,没 ...
- (转)oracle使用expdp、impdp和exp、imp导入导出表及表结构
使用expdp.impdp和exp.imp时应该注重的事项: 1.exp和imp是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用. 2.expdp和impdp是服务端的工具程序,他们只能在 ...
- 【CERC 2014 E】2048
题意 2048曾经是一款风靡全球的小游戏. 今天,我们换一种方式来玩这个小游戏. 你有一个双端队列,你只能把元素从左端或从右端放入双端队列中.一旦放入就不得取出.放入后,若队列中有连续两个相同的元素, ...
- Office365 PowerShell打开邮箱审计功能
最近总公司要求Office365需要在所有的邮箱上面打开审计功能.这个功能没法通过图形界面操作,只能通过powershell脚本实现. 微软提供了一个官方的脚本,不过里面有个小bug https:// ...