JUC同步锁原理源码解析五----Phaser
JUC同步锁原理源码解析五----Phaser
Phaser
Phaser的来源
A reusable synchronization barrier, similar in functionality to {@link java.util.concurrent.CyclicBarrier CyclicBarrier} and {@link java.util.concurrent.CountDownLatch CountDownLatch} but supporting more flexible usage.
JDK中对Phaser的定义时,一个可重用的同步栅栏。其作用相当于CyclicBarrier和CountDownLatch的结合体,但是支持更加灵活的使用
Phaser的底层实现
Phaser的底层实现依旧依赖于CAS的自旋锁操作,通过cas保证原子性的操作
2.Phaser
基本使用
import java.util.List;
import java.util.concurrent.Phaser;
public class PhaserDemo {
void runTasks(List<Runnable> tasks) {
final Phaser phaser = new Phaser(1); // "1" to register self
// create and start threads
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
phaser.arriveAndAwaitAdvance(); // await all creation
task.run();
}
}.start();
}
}
void startTasks(List<Runnable> tasks, int iterations) {
Phaser phaser = new Phaser() {
protected boolean onAdvance(int phase, int registeredParties) {
return phase >= iterations - 1 || registeredParties == 0;
}
};
phaser.register();
for (Runnable task : tasks) {
phaser.register();
new Thread(() -> {
do {
task.run();
phaser.arriveAndAwaitAdvance();
} while (!phaser.isTerminated());
}).start();
}
// allow threads to proceed; don't wait for them
phaser.arriveAndDeregister();
}
}
Phaser类
public class Phaser {
private volatile long state;//采用long 64 位表示state变量。使用位操作来表示,cas单原子性变量保证多变量的原子性
private static final int MAX_PARTIES = 0xffff;
private static final int MAX_PHASE = Integer.MAX_VALUE;
private static final int PARTIES_SHIFT = 16;
private static final int PHASE_SHIFT = 32;
private static final int UNARRIVED_MASK = 0xffff; // to mask ints
private static final long PARTIES_MASK = 0xffff0000L; // to mask longs
private static final long COUNTS_MASK = 0xffffffffL;
private static final long TERMINATION_BIT = 1L << 63;
// some special values
private static final int ONE_ARRIVAL = 1;
private static final int ONE_PARTY = 1 << PARTIES_SHIFT;
private static final int ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY;
private static final int EMPTY = 1;
// The following unpacking methods are usually manually inlined
private static int unarrivedOf(long s) {
int counts = (int)s;
return (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
}
private static int partiesOf(long s) {
return (int)s >>> PARTIES_SHIFT;
}
private static int phaseOf(long s) {
return (int)(s >>> PHASE_SHIFT);
}
private static int arrivedOf(long s) {
int counts = (int)s;
return (counts == EMPTY) ? 0 :
(counts >>> PARTIES_SHIFT) - (counts & UNARRIVED_MASK);
}
/**
* The parent of this phaser, or null if none
*/
private final Phaser parent;
/**
* The root of phaser tree. Equals this if not in a tree.
*/
private final Phaser root;
/**
* Heads of Treiber stacks for waiting threads. To eliminate
* contention when releasing some threads while adding others, we
* use two of them, alternating across even and odd phases.
* Subphasers share queues with root to speed up releases.
*/
private final AtomicReference<QNode> evenQ;
private final AtomicReference<QNode> oddQ;
QNode类
static final class QNode implements ForkJoinPool.ManagedBlocker {
final Phaser phaser;
final int phase;
final boolean interruptible;
final boolean timed;
boolean wasInterrupted;
long nanos;
final long deadline;
volatile Thread thread; // nulled to cancel wait
QNode next;
QNode(Phaser phaser, int phase, boolean interruptible,
boolean timed, long nanos) {
this.phaser = phaser;
this.phase = phase;
this.interruptible = interruptible;
this.nanos = nanos;
this.timed = timed;
this.deadline = timed ? System.nanoTime() + nanos : 0L;
thread = Thread.currentThread();
}
Phaser的构造器
public Phaser(int parties) {
this(null, parties);
}
public Phaser(Phaser parent) {
this(parent, 0);
}
//最终都是走这个构造器方法
public Phaser(Phaser parent, int parties) {
if (parties >>> PARTIES_SHIFT != 0)//
throw new IllegalArgumentException("Illegal number of parties");
int phase = 0;
this.parent = parent;
if (parent != null) {//判断父阶段是否为空。如果有父阶段,子阶段的行为由父阶段控制,调用父阶段去处理
final Phaser root = parent.root;//root为父阶段
this.root = root;
this.evenQ = root.evenQ;//使用父阶段的偶队列
this.oddQ = root.oddQ;//使用父阶段的奇队列
if (parties != 0)//如果父阶段不为空
phase = parent.doRegister(1);//将当前阶段注册到父阶段中
}
else {//表示没有父阶段
this.root = this;
this.evenQ = new AtomicReference<QNode>();
this.oddQ = new AtomicReference<QNode>();
}
this.state = (parties == 0) ? (long)EMPTY :
((long)phase << PHASE_SHIFT) | //64位中高32位表示阶段数,也即phase的数量
((long)parties << PARTIES_SHIFT) | //64位中低32位的高16位表示参与者的数量
((long)parties);//64位中低32位的低16位表示未完成的数量
}
register方法
public int register() {
return doRegister(1);
}
private int doRegister(int registrations) {
// adjustment to state
long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;//对当前state变量的参与数量和未完成数量都加 1
final Phaser parent = this.parent;//如果有父阶段,获取父阶段
int phase;
for (;;) {
long s = (parent == null) ? state : reconcileState();//拿到state的值
int counts = (int)s;//将64位取低32位的值
int parties = counts >>> PARTIES_SHIFT;//右移16位,取高16位的值,也即parties的数量
int unarrived = counts & UNARRIVED_MASK;//获取低16位的数值,也即未到达的数量
if (registrations > MAX_PARTIES - parties)//越界判断
throw new IllegalStateException(badRegister(s));
phase = (int)(s >>> PHASE_SHIFT);//获取阶段数
if (phase < 0)//阶段数为0,表示已经超过阶段数了,不需要继续处理了
break;
if (counts != EMPTY) { // not 1st registration
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance 如果未完成数量等于0
root.internalAwaitAdvance(phase, null);//阻塞等待或者等到下一阶段推进
else if (UNSAFE.compareAndSwapLong(this, stateOffset,//将当前需要参与的数量放到state变量中
s, s + adjust))
break;//退出循环
}
}
else if (parent == null) {//没有父阶段或自己就是父阶段
long next = ((long)phase << PHASE_SHIFT) | adjust; //阶段数量增加
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))//cas尝试将阶段数量增加,成功就腿很粗
break;
}
else {
synchronized (this) { //走到这里表示,自身属于子阶段,需要接受父阶段的调度
if (state == s) { //重新检测state变量是否改变
phase = parent.doRegister(1);//向父阶段注册
if (phase < 0)//阶段数已经超过了最大阶段数
break;
//while循环,设置state的中phase阶段数直至成功
while (!UNSAFE.compareAndSwapLong
(this, stateOffset, s,
((long)phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
// assert (int)s == EMPTY;
}
break;
}
}
}
}
return phase;
}
reconcileState方法:
//只要使用在有父子阶段的存在的情况下
private long reconcileState() {
final Phaser root = this.root;//获取到当前阶段
long s = state;//取得当前state
if (root != this) {//如果root不是当前阶段
int phase, p;
// CAS to root phase with current parties, tripping unarrived
while ((phase = (int)(root.state >>> PHASE_SHIFT)) != //phase等于root的阶段数
(int)(s >>> PHASE_SHIFT) &&//root的阶段数不等于当前阶段state变量的阶段数
!UNSAFE.compareAndSwapLong//
(this, stateOffset, s,
s = (((long)phase << PHASE_SHIFT) | //root的阶段数
((phase < 0) ? (s & COUNTS_MASK) : //如果阶段数已经超了,直接取低32位
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY : //获取到s对应的parties数量,复制给p
((s & PARTIES_MASK) | p))))))
s = state;
}
return s;
}
arriveAndAwaitAdvance方法
public int arriveAndAwaitAdvance() {
// Specialization of doArrive+awaitAdvance eliminating some reads/paths
final Phaser root = this.root;//获取当前阶段
for (;;) {
long s = (root == this) ? state : reconcileState();//获取到state的状态,如果有父阶段调用reconcileState
int phase = (int)(s >>> PHASE_SHIFT);//等到当前阶段数
if (phase < 0)//阶段数超了,直接返回
return phase;
int counts = (int)s;//获取state的低32位
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);//获取到未到达的参与者数量
if (unarrived <= 0)//未到达的参与者数量越界检查
throw new IllegalStateException(badArrive(s));
if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
s -= ONE_ARRIVAL)) {//cas将未到达的数量减1
if (unarrived > 1)//如果未到达的数量大于1
return root.internalAwaitAdvance(phase, null);//调用父阶段控制其去睡眠等待
if (root != this)//如果this不是父阶段
return parent.arriveAndAwaitAdvance();//由父类处理,将到达线程数减1或滚动到下一阶段
long n = s & PARTIES_MASK; // base of next state//获得参与者parties数量
int nextUnarrived = (int)n >>> PARTIES_SHIFT;//获得下一个阶段参与者的数量
if (onAdvance(phase, nextUnarrived))//调用onAdvance会掉方法
n |= TERMINATION_BIT;//TERMINATION_BIT:1<<63,标识阶段数结束
else if (nextUnarrived == 0)//如果下一个阶段参与者为0
n |= EMPTY;//异或上EMPTY
else
n |= nextUnarrived;//否则将低32位的低16位置为下一阶段参与者的数量,表示未完成的数量等于下一个阶段参与者的数量
int nextPhase = (phase + 1) & MAX_PHASE;//获得下一个阶段的phase的数量
n |= (long)nextPhase << PHASE_SHIFT;//n异或上下一阶段phase的数量组合成state比那辆
if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))//cas设置state变量。当前线程如果设置state变量失败,是否可以允许爆炸唤醒,不直接退出?
return (int)(state >>> PHASE_SHIFT); // cas失败,返回state中的阶段数
releaseWaiters(phase);//释放等待线程
return nextPhase;
}
}
}
internalAwaitAdvance方法
private int internalAwaitAdvance(int phase, QNode node) {
// assert root == this;
releaseWaiters(phase-1); // ensure old queue clean 将上一个阶段等待线程唤醒,将队列清空
boolean queued = false; // true when node is enqueued
int lastUnarrived = 0; // to increase spins upon change
int spins = SPINS_PER_ARRIVAL;//SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8,单核CPU没有自旋的必要,浪费时间
long s;
int p;
while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {//判断当前阶段数是否等于phase
if (node == null) { // spinning in noninterruptible mode
int unarrived = (int)s & UNARRIVED_MASK;//获取未到达的参与者数量
if (unarrived != lastUnarrived &&//未到达的参与者数量不等于lastUnarrived
(lastUnarrived = unarrived) < NCPU)//lastUnarrived 赋值lastUnarrived。小于CPU的核心数,证明任务很快可以调度,值得等待。但是考虑业务线程,实际中如果CPU的核心数没有大于2,其实没有自旋的必要。
spins += SPINS_PER_ARRIVAL;//增加自旋次数
boolean interrupted = Thread.interrupted();//判断中断标志位
if (interrupted || --spins < 0) { // need node to record intr //如果中断了,或者自旋次数小于0
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;//将中断标识赋值
}
}
else if (node.isReleasable()) // done or aborted 判断是否已经完成,或者说中断等方式释放
break;
else if (!queued) { // push onto queue 不在队列中
AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ; //根据phase的奇偶性,选择队列
QNode q = node.next = head.get();//头插法
if ((q == null || q.phase == phase) &&
(int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
queued = head.compareAndSet(q, node);
}
else {
try {
ForkJoinPool.managedBlock(node);//由于兼容forkJoin线程池,所以这里提供模板。这里进行阻塞等待
} catch (InterruptedException ie) {
node.wasInterrupted = true;
}
}
}
if (node != null) {//进入这里表示,当前阶段不一致
if (node.thread != null)
node.thread = null; // avoid need for unpark() //将thread置为空
if (node.wasInterrupted && !node.interruptible) //节点被中断,并且节点不可中断
Thread.currentThread().interrupt();//重置中断标志位
if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase)//当前阶段数量一致,也即属于同一阶段
return abortWait(phase); // possibly clean up on abort
}
releaseWaiters(phase);//唤醒等待的线程
return p;
}
releaseWaiters方法
private void releaseWaiters(int phase) {
QNode q; // first element of queue
Thread t; // its thread
AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;//根据阶段数判断是奇数队列还是偶数队列
while ((q = head.get()) != null &&//获取到头结点,如果头结点补位空
q.phase != (int)(root.state >>> PHASE_SHIFT)) {//并且当前阶段数已经滚动到下一个阶段
if (head.compareAndSet(q, q.next) &&//cas替换头结点
(t = q.thread) != null) {//如果旧的头结点不为空
q.thread = null;//将节点q的线程置为空
LockSupport.unpark(t);//唤醒节点q的线程
}
}
}
arriveAndDeregister方法
public int arriveAndDeregister() {
return doArrive(ONE_DEREGISTER);//ONE_DEREGISTER = ONE_ARRIVAL|ONE_PARTY;
}
doArrive方法
private int doArrive(int adjust) {
final Phaser root = this.root;//获取当前阶段
for (;;) {
long s = (root == this) ? state : reconcileState();//如果有父阶段获取父阶段的state,没有取当前阶段的state
int phase = (int)(s >>> PHASE_SHIFT);//获取阶段数
if (phase < 0)//如果阶段数已经小于0,表示已经结束,直接返回
return phase;
int counts = (int)s;//获取state的低32位,业绩参与者和未完成的参与者
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);//获取未到达的参与者数量EMPTY是特殊值,表示没有未到达的参与者
if (unarrived <= 0)//如果未到达的参与者小于0,非法直接抛出异常
throw new IllegalStateException(badArrive(s));
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {//直接cas自旋,更新state变量
if (unarrived == 1) {//如果当前线程是最后一个未完成的参与者,需要做收尾工作
long n = s & PARTIES_MASK; // base of next state 获取参与者的数量
int nextUnarrived = (int)n >>> PARTIES_SHIFT;//设置未到达的参与者数量为下一个阶段的参与者数量
if (root == this) {//如果root是当前阶段
if (onAdvance(phase, nextUnarrived))//回调钩子函数
n |= TERMINATION_BIT;//置为TERMINATION状态,TERMINATION_BIT = 1L << 63;最高位符号位表示终止标志位
else if (nextUnarrived == 0)//如果下一个阶段没有参与者
n |= EMPTY;//直接或上一个EMPTY
else
n |= nextUnarrived;//否则直接或上下一个阶段的未达到的参与者数量
int nextPhase = (phase + 1) & MAX_PHASE;//阶段数加1
n |= (long)nextPhase << PHASE_SHIFT;//将阶段数组合到变量n中,
UNSAFE.compareAndSwapLong(this, stateOffset, s, n);//cas自旋将state置为n,表示滚动到下一个阶段
releaseWaiters(phase);//释放所有等待的节点
}
else if (nextUnarrived == 0) { // propagate deregistration 这里表示root有父阶段且自己已经完成
phase = parent.doArrive(ONE_DEREGISTER);//父阶段中标识自己已经完成并且将参与者数量减1,未到达的参与者也减1
UNSAFE.compareAndSwapLong(this, stateOffset,//将当前的state,case自旋,置为EMPTY
s, s | EMPTY);
}
else
phase = parent.doArrive(ONE_ARRIVAL);//当前线程不是最后一个完成的线程,将未到达的参与者数量减1即可
}
return phase;
}
}
}
4.留言
到了这里,其实AQS的源码基本已经覆盖了,对于AQS的源码也应该有了清楚的认知。总结就是:一个volatile 的state变量,两个等待队列(竞争队列,条件队列),通过cas的方式保证单变量的原子性。后续将会对Exchanger以及Phaser进行源码解析,到此基本AQS已经到了一个段落了。后续观看源码时,请注意多考虑一下多线程并发时可能出现的情况,去理解doug lea写代码的思路。
JUC同步锁原理源码解析五----Phaser的更多相关文章
- 从ReentrantLock详解AQS原理源码解析
数据结构 java.util.concurrent.locks.AbstractQueuedSynchronizer类中存在如下数据结构. // 链表结点 static final class Nod ...
- 【Spring实战】Spring注解配置工作原理源码解析
一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...
- 【转】【Spring实战】Spring注解配置工作原理源码解析
一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...
- Spring Boot中@ConfigurationProperties注解实现原理源码解析
0. 开源项目推荐 Pepper Metrics是我与同事开发的一个开源工具(https://github.com/zrbcool/pepper-metrics),其通过收集jedis/mybatis ...
- Spring注解Component原理源码解析
在实际开发中,我们经常使用Spring的@Component.@Service.@Repository以及 @Controller等注解来实现bean托管给Spring容器管理.Spring是怎么样实 ...
- vue双向绑定原理源码解析
当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/maxlove123 ...
- 设计模式课程 设计模式精讲 8-8 单例设计模式-Enum枚举单例、原理源码解析以及反编译实战
1 课堂解析 2 代码演练 2.1 枚举类单例解决序列化破坏demo 2.2 枚举类单例解决序列化破坏原理 2.3 枚举类单例解决反射攻击demo 2.4 枚举类单例解决反射攻击原理 3 jad的使用 ...
- java基础(十八)----- java动态代理原理源码解析
关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 静态代理 1.静态代理 静态代理:由程序员创建或特定工 ...
- ReentrantLock(重入锁)的源码解析
转自:从源码角度彻底理解ReentrantLock(重入锁)](https://www.cnblogs.com/takumicx/p/9402021.html)) 公平锁内部是FairSync,非公平 ...
- Celery 源码解析五: 远程控制管理
今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...
随机推荐
- 数据挖掘决策树—R实现
决策树 决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别.分类树(决策树)是一种十分常用的分类方法.它是一种监督学习,所谓监督学习就是给定一 ...
- Spring 源码阅读之标签解析
全局目录.md 引子 1.容器最基本使用.md 系列1 - bean 标签解析: 2.XmlBeanFactory 的类图介绍.md 3.XmlBeanFactory 对xml文件读取.md 4.xm ...
- panda之series结构
eries 结构,也称 Series 序列,是 Pandas 常用的数据结构之一,它是一种类似于一维数组的结构,由一组数据值(value)和一组标签组成,其中标签与数据值之间是一一对应的关系.Seri ...
- Java运行准备JDR JRE JVM知识和环境变量的作用
JDK.JRE.JVM简介 JDK:Java Development Kit Java开发者工具包 JRE:Java runtime environment Java运行环境 JVM:Java V ...
- 访问nginx报错502日志:failed (13: Permission denied) while connecting to upstream
1.错误问题 nginx启动成功,但是访问nginx报错502.检查后台项目,使用IP+端口可以正常访问项目的,这说明项目启动成功了.那就是nginx的问题.检查了nginx.conf文件发现配置的反 ...
- 希望所有计算机学生能看到这篇c语言教程
大部分程序员走入编程世界第一个学习的语言就是C语言. 作为一门古老的编程语言,c语言拥有48年的发展历程. 为什么要学习 C语言? C语言是学习计算机程序设计语言的入门语言.最全面的编程面试网站 C语 ...
- 2023-02-13:力扣数据中心有 n 台服务器,分别按从 0 到 n-1 的方式进行了编号 它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群 其中连接 connections 是
2023-02-13:力扣数据中心有 n 台服务器,分别按从 0 到 n-1 的方式进行了编号 它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群 其中连接 connections 是 ...
- 2022-01-21:完美矩形。 给你一个数组 rectangles ,其中 rectangles[i] = [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi,
2022-01-21:完美矩形. 给你一个数组 rectangles ,其中 rectangles[i] = [xi, yi, ai, bi] 表示一个坐标轴平行的矩形.这个矩形的左下顶点是 (xi, ...
- 2021-07-17:一个不含有负数的数组可以代表一圈环形山,每个位置的值代表山的高度。比如, {3,1,2,4,5}、{4,5,3,1,2}或{1,2,4,5,3}都代表同样结构的环形山。山峰A和山
2021-07-17:一个不含有负数的数组可以代表一圈环形山,每个位置的值代表山的高度.比如, {3,1,2,4,5}.{4,5,3,1,2}或{1,2,4,5,3}都代表同样结构的环形山.山峰A和山 ...
- 2021-08-08:自由之路。电子游戏“辐射4”中,任务“通向自由”要求玩家到达名为“Freedom Trail Ring”的金属表盘,并使用表盘拼写特定关键词才能开门。给定一个字符串 ring,表
2021-08-08:自由之路.电子游戏"辐射4"中,任务"通向自由"要求玩家到达名为"Freedom Trail Ring"的金属表盘,并 ...