简介

Phaser,阶段器,可作为一个可复用的同步屏障,与CyclicBarrier和CountDownLatch类似,但更强大。

全览图

如上图所示,phaser,支持phaser树(图中,简化为phaser链表模式,独子单传,后文也称phaser链)模式,分摊并发的压力。每个phaser结点的father指针指向前一个phaser结点,最前头的结点成为root结点,其father指针指向null, 每一个结点的root指针指向root结点,root结点的root指针指向它自己。

只有root结点的evenQ和oddQ分别指向两个QNode链表。每个QNode结点包含有phaser和thread等关键熟悉,其中,thread指向当前线程,phaser指向当前线程所注册的phaser。

这两个链表里的线程所对应的phase(阶段)要么都为奇数,要么都为偶数,相邻阶段的两组线程一定在不同的链表里面,这样在新老阶段更迭时,操作的是不同的链表,不会错乱。整个phaser链,共用这两个QNode链。

而且,线程也只会在root结点上被封装进QNode结点入栈(QNode链,入栈,FIFO,后文有时也叫入队,不影响功能),每个phaser在初始时(被第一个线程注册时)以当前线程向其父phaser注册的方式与其父phaser建立联系,当此phaser上的线程都到达了,再以当前线程(最后一个抵达的线程)通知其父phaser,自己这边OK了,每个phaser都以同样的方式通知其父phaser,最后到达root phaser,开始唤醒睡在栈里(QNode链表)的线程,准备进入下一阶段。

phaser的关键属性state,是一个64位的long类型数据,划分为4个域:

  • unarrived   -- 还没有抵达屏障的参与者的个数 (bits 0-15)
  • parties       -- 需要等待的参与者的个数            (bits 16-31)
  • phase        -- 屏障所处的阶段                          (bits 32-62)
  • terminated -- 屏障的结束标记                          (bit 63 / sign)

特别地,初始时,state的值为1,称为EMPTY,也即是unarrived = 1,其余都为0,这是一个标记,表示此phaser还没有线程来注册过,EMPTY = 1,而不是0,是因为0有特殊的含意,可能表示所有的线程都到达屏障了,此时unarrived也为0(而不是初始状态),正常来讲,parties表示总的注册的线程的个数,大于等于unarrived,初始时,parties = 0,而unarrived = 1,更易于辨别。

源码分析

属性

     /*
* unarrived -- 还没有抵达屏障的参与者的个数 (bits 0-15)
* parties -- 需要等待的参与者的个数 (bits 16-31)
* phase -- 屏障所处的阶段 (bits 32-62)
* terminated -- 屏障的结束标记 (bit 63 / sign)
*/
private volatile long state; 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; // 与int值与得未抵达屏障的参与者个数
private static final long PARTIES_MASK = 0xffff0000L; // 与long值与得参与者个数
private static final long COUNTS_MASK = 0xffffffffL; // 与之相与的unarrived和parties两部分值
private static final long TERMINATION_BIT = 1L << 63; // 终结位 private static final int ONE_ARRIVAL = 1; // 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; // 初始值 private final Phaser parent; // 指向父phaser
private final Phaser root; // 指向root phaser private final AtomicReference<QNode> evenQ; // 偶数phase的栈(线程)
private final AtomicReference<QNode> oddQ; // 奇数phase的栈(线程)

对于主状态,为了有效地维护原子性,这些值被打包成一个单独的(原子)数据(long类型),编码简单高效,竞争窗口(空间)小。

构造方法

     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) { // 父phaser不为空
final Phaser root = parent.root;
this.root = root; // 指向root phaser
this.evenQ = root.evenQ; // 两个栈,整个phaser链只有一份
this.oddQ = root.oddQ;
if (parties != 0)
phase = parent.doRegister(1); // 向父phaser注册当前线程
} else {
this.root = this; // 否则,自己是root phaser
this.evenQ = new AtomicReference<QNode>(); // 负责创建两个栈(QNode链)
this.oddQ = new AtomicReference<QNode>();
}
// 更新状态
this.state = (parties == 0) ? (long) EMPTY
: ((long) phase << PHASE_SHIFT) | ((long) parties << PARTIES_SHIFT) | ((long) parties);
}

关键方法

doRegister(int)

     private int doRegister(int registrations) {
long adjust = ((long) registrations << PARTIES_SHIFT) | registrations; // 调整主状态的因子,parties | unarrived
final Phaser parent = this.parent;
int phase;
for (;;) {
long s = (parent == null) ? state : reconcileState(); // reconcileState()方法是调整当前phaser的状态与root的一致
int counts = (int) s;
int parties = counts >>> PARTIES_SHIFT;
int unarrived = counts & UNARRIVED_MASK;
if (registrations > MAX_PARTIES - parties)
throw new IllegalStateException(badRegister(s));
phase = (int) (s >>> PHASE_SHIFT);
if (phase < 0)
break;
if (counts != EMPTY) { // 当前线程不是此phaser的第一次注册
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // 上一阶段已经结束
root.internalAwaitAdvance(phase, null); // 等待进入到下一阶段
else if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s + adjust)) // 否则,CAS调整主状态
break;
}
} else if (parent == null) { // 当前phaser是root phaser
long next = ((long) phase << PHASE_SHIFT) | adjust; // 调整因子,phase | parties | unarrived
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next)) // CAS调整主状态
break;
} else {
synchronized (this) { // 第一个子phaser注册,需要加锁,不管多少个线程在子phaser上注册,而只需一个线程在其父phaser上注册
if (state == s) { // 加锁后,再次检查,看是否别的线程已经更新过主状态了
phase = parent.doRegister(1); // 向其父phaser注册(获得锁的当前线程)
if (phase < 0) // phaser链已结束,直接退出
break;
// 更新主状态
while (!UNSAFE.compareAndSwapLong(this, stateOffset, s,
((long) phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int) (root.state >>> PHASE_SHIFT);
}
break;
}
}
}
}
return phase;
}

arriveAndAwaitAdvance()

     public int arriveAndAwaitAdvance() {
final Phaser root = this.root;
for (;;) {
long s = (root == this) ? state : reconcileState(); // 主状态
int phase = (int) (s >>> PHASE_SHIFT); // 当前阶段
if (phase < 0) // 已结束,退出
return phase;
int counts = (int) s;
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更新主状态
if (unarrived > 1) // 还有未到达的线程
return root.internalAwaitAdvance(phase, null); // 等待,或自旋,或入栈阻塞
if (root != this)
return parent.arriveAndAwaitAdvance(); // 说明当前phaser上的所有线程都已经抵达,那么通知父phaser(其实作为一个整体【线程】,到达父phaser的屏障,具有传递性)
long n = s & PARTIES_MASK; // 当前phaser的总的参与者个数
int nextUnarrived = (int) n >>> PARTIES_SHIFT; // 作为下一阶段的未抵达参与者个数
if (onAdvance(phase, nextUnarrived)) // 冲破屏障时调用的方法,返回true,则结束phaser
n |= TERMINATION_BIT;
else if (nextUnarrived == 0) // 如果没有参与者了,恢复初始值EMPTY
n |= EMPTY;
else
n |= nextUnarrived; // 最后一种情况,phaser | unarrived
int nextPhase = (phase + 1) & MAX_PHASE; // 下一个阶段
n |= (long) nextPhase << PHASE_SHIFT; // phase | phaser | unarrived
if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n)) // 更新主状态
return (int) (state >>> PHASE_SHIFT); // 有竞争,直接返回
releaseWaiters(phase); // 否则,释放掉阻塞在此阶段上的所有线程
return nextPhase;
}
}
}

internalAwaitAdvance(int phase, QNode node)

     private int internalAwaitAdvance(int phase, QNode node) {
releaseWaiters(phase - 1); // 确保老的队列里的线程全部释放了
boolean queued = false; // 标识是否成功入队
int lastUnarrived = 0; // 记录上次未到达参与者(线程)的个数,如果发生变化,则增加自旋次数(说不定马上结束了呢,这样就不用阻塞了)
int spins = SPINS_PER_ARRIVAL; // 自旋次数
long s;
int p;
while ((p = (int) ((s = state) >>> PHASE_SHIFT)) == phase) {
if (node == null) { // 在不可中断模式下自旋
int unarrived = (int) s & UNARRIVED_MASK;
// 如果未到达参与者数量发生了变化,且变化后的未到达数量小于CPU核数,需要增加自旋次数
if (unarrived != lastUnarrived && (lastUnarrived = unarrived) < NCPU)
spins += SPINS_PER_ARRIVAL;
boolean interrupted = Thread.interrupted(); // 获取并清除当前线程的中断标识
if (interrupted || --spins < 0) { // 如果当前线程被中断,或者自旋次数用完,创建一个结点,入队准备进入阻塞
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
} else if (node.isReleasable()) // 完成或放弃
break;
else if (!queued) { // 入队
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)
queued = head.compareAndSet(q, node);
} else {
try {
ForkJoinPool.managedBlock(node); // 阻塞,其实调用的是QNode的block()方法,最终还是LockSupport.park()方法
} catch (InterruptedException ie) {
node.wasInterrupted = true; // 记录中断
}
}
} if (node != null) {
if (node.thread != null)
node.thread = null; // 被唤醒后,置空thread引用,避免再次unpark
if (node.wasInterrupted && !node.interruptible) // 不可中断模式下,传递中断
Thread.currentThread().interrupt();
if (p == phase && (p = (int) (state >>> PHASE_SHIFT)) == phase)
return abortWait(phase); // 依旧没有进入到下一个状态,清除那些由于超时或中断不再等待下一阶段的结点
}
releaseWaiters(phase); // 唤醒阻塞的线程
return p;
}

doArrive(boolean deregister)

     private int doArrive(int adjust) {
final Phaser root = this.root;
for (;;) {
long s = (root == this) ? state : reconcileState(); // 主状态
int phase = (int) (s >>> PHASE_SHIFT); // 阶段
if (phase < 0) // 已结束,退出
return phase;
int counts = (int) s;
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); // 未抵达线程个数
if (unarrived <= 0) // 非法值
throw new IllegalStateException(badArrive(s));
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s -= adjust)) { // CAS更新主状态
if (unarrived == 1) { // 如果未到达参与者的个数是1
long n = s & PARTIES_MASK; // 当前phaser的总的参与者个数
int nextUnarrived = (int) n >>> PARTIES_SHIFT; // 作为下一阶段的未抵达参与者个数
if (root == this) { // 如果当前phaser是root
if (onAdvance(phase, nextUnarrived)) // 冲破屏障时调用的方法,返回true,则结束phaser
n |= TERMINATION_BIT;
else if (nextUnarrived == 0) // 如果没有参与者了,恢复初始值EMPTY
n |= EMPTY;
else // 最后一种情况,phaser | unarrived
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE; // 下一个阶段
n |= (long) nextPhase << PHASE_SHIFT; // phase | phaser | unarrived
UNSAFE.compareAndSwapLong(this, stateOffset, s, n); // 更新主状态
releaseWaiters(phase); // 释放掉阻塞在此阶段上的所有线程
} else if (nextUnarrived == 0) { // 如果没有参与者了,从父phaser上注销(传递)
phase = parent.doArrive(ONE_DEREGISTER);
UNSAFE.compareAndSwapLong(this, stateOffset, s, s | EMPTY); // 调整主状态
} else
phase = parent.doArrive(ONE_ARRIVAL); // 传递调用父phaser的doArrive方法
}
return phase;
}
}
}

此方法,与arriveAndAwaitAdvance()类似,但不阻塞,可能会有注销操作。

经典应用

     void build(Task[] tasks, int lo, int hi, Phaser ph) {
if (hi - lo > TASKS_PER_PHASER) {
for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
int j = Math.min(i + TASKS_PER_PHASER, hi);
build(tasks, i, j, new Phaser(ph));
}
} else {
for (int i = lo; i < hi; ++i)
tasks[i] = new Task(ph);
}
}

一组任务,一个phaser树,对这组任务进行分段,每一段任务挂到一个phaser上。

行文至此结束。

尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_phaser.html

【JUC源码解析】Phaser的更多相关文章

  1. 【JUC源码解析】ScheduledThreadPoolExecutor

    简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...

  2. 【JUC源码解析】SynchronousQueue

    简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...

  3. 【JUC源码解析】ForkJoinPool

    简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...

  4. 【JUC源码解析】DelayQueue

    简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...

  5. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

  6. 【JUC源码解析】ConcurrentLinkedQueue

    简介 ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列. 概述 队列顺序,为FIFO(first-in-first-out):队首元素,是当前排队时间最长的:队尾元素,当 ...

  7. 【JUC源码解析】Exchanger

    简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...

  8. Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue

    功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...

  9. Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue

    功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...

随机推荐

  1. swift版的StringAttribute

    swift版的StringAttribute 效果 源码 https://github.com/YouXianMing/Swift-StringAttribute // // StringAttrib ...

  2. SCCM OS播发

    SCCM OS播发1.在分发点启用PXE支持2.将启动映像包分发到分发点:需要将x86和x64都分发到分发点,如果只分发x64,在客户端pxe启动时会出现 no response from wds s ...

  3. 【matlab】 幂法 求解最大特征值

    一. 算法: 1.输入矩阵A,初始向量x误差限ep,最大迭代次数N 2.置 k = 1, m1 = 0; 3.求Xr-> norm(x)   abs(Xr)=max[Xi] 1<=i< ...

  4. handsontable 和 echarts都定义了require方法,初始化时冲突了,怎么办?

    echarts初始化时报这个错误. require.config is not a function  方案一: 让其中一方的初始化不依赖于 require即可 1.去掉 var testDrowEc ...

  5. Hadoop HBase概念学习系列之HBase里的高表设计概念(表设计)(二十八)

    在下面这篇博文里,我给各位博客们,分享了创建HBase表,但这远不止打好基础. HBase编程 API入门系列之create(管理端而言)(8) 在关系型数据库里,表的高表和宽表是不存在的.在如HBa ...

  6. ZT 人生真的是一场马拉松吗?

    中国合伙人:孟晓俊:生活应该是什么样的?自己提出的问题应该由自己来回答,别人的回答是别人的答案,是别人的生活,而你应该过自己的生活,不是别人的生活.     人生真的是一场马拉松吗? 投递人 itwr ...

  7. jetbrains全家桶永久激活大法

    不得不说jetbrains的产品真的挺好用的,比如耳熟能详的idea和pycharm等等,但正版的费用真的非我等学生党所能承担,网上也有一些注册码的教程,原理是通过服务器进行注册认证,但貌似目前用的比 ...

  8. react中受控组件相关的warning

    在表单中,报如下的错,意思是非受控的输入框变成了受控的,报错信息如下 Warning: A component is changing an uncontrolled input of type te ...

  9. Linux禁用root账户ssh登录

    前言 今天登录服务器的时候,控制台输出如下信息 There were 48990 failed login attempts since the last successful login. Last ...

  10. java xml文件

    xml: 是可扩展的标签语言.其中标签可以自定义. 作用是存储数据,即配置文件. 书写规范: 1:区分大小写,html不区分. 2:应该有根标签(类似html的<html>标签) 3:标签 ...