文章目录
系列文章
JUC并发工具包之Phaser Part1 结构与状态
JUC并发工具包之Phaser Part2 源码分析
JUC并发工具包之Phaser Part3 场景使用
JUC并发工具包之Semaphore
JUC并发工具包之CyclicBarrier
JUC并发工具包之CountDownLatch
JUC并发工具包之CyclicBarrier & CountDownLatch的异同
预备知识
读Phaser的源码很大的一个特点就是位运算太多,还有就是QNode使用的是Treiber Stack
,建议先看下另外几篇文章,会很有帮助。
- Java常用的二进制位操作(左移、右移、无符号右移、加法、减法)
- Java中的位掩码BitMask
- 理解与使用Treiber Stack
- 线程池中状态与线程数的设计分析(ThreadPoolExecutor中ctl变量)
主要方法
new Phaser()
我们调用的是new Phaser(int parties)
,内部parent不考虑,传null
public Phaser(Phaser parent, int parties) {
// 因为parties的二进制位数只有16位(bits 16-31),所以正常的parties无符号右移16位应为0
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;
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>();
}
// 如果parties为0,表示没有参与者,只有使用默认的EMPTY当作状态
// 否则做与运算,假设传入parties为1
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0000, 0000 0000 0000 0000 # (long)phase << PHASE_SHIFT
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0000 # (long)parties << PARTIES_SHIFT
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0000, 0000 0000 0000 0001 # (long)parties
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # state
// 最终的结果表示phase为0,parties为1,unarrived为1
this.state = (parties == 0) ? (long)EMPTY :
((long)phase << PHASE_SHIFT) |
((long)parties << PARTIES_SHIFT) |
((long)parties);
}
register()即doRegister(int registrations)
我们只能调用register()方法,所以传入的registrations为1:
private int doRegister(int registrations) {
// 1左移16位表达的正是1个parties的值,最终的结果不光表示了1个parties,还表示了一个unarrived
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0000 # (long)registrations << PARTIES_SHIFT
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0000, 0000 0000 0000 0001 # registrations
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # adjust
long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
final Phaser parent = this.parent;
int phase;
for (;;) {
// 暂不考虑parent的情况,s即为state
long s = (parent == null) ? state : reconcileState();
// 截取低32位计算parties和unarrived值,Part1有说明
int counts = (int)s;
int parties = counts >>> PARTIES_SHIFT;
int unarrived = counts & UNARRIVED_MASK;
// 当然新注册的parties数得要在MAX_PARTIES之内
if (registrations > MAX_PARTIES - parties)
throw new IllegalStateException(badRegister(s));
// 计算phase,Part1有说明
phase = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
break;
// 构造函数中传入parties为0时,counts==EMPTY条件达成,表示第一次注册
if (counts != EMPTY) { // not 1st registration
// 构造函数中传入过parties,当前再次register其他的parties
if (parent == null || reconcileState() == s) {
// unarrived等于0说明当前阶段正在执行onAdvance()方法,等待其执行完毕
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
// 新registrations的adjust和s做异或运算得到当前的状态值
// 不进位加法其实就是一个异或操作、进位加法其实就是一个与操作的结果左移一位
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # s 假设之前new Phaser(1)
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # adjust
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # 先与操作,发现需要进位
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0010, 0000 0000 0000 0010 # 再左移即为最后的状态值
// 所以最终的parties为2,unarrived为2,说实话,不把二进制运算贴出来,我自己都看晕了。
else if (UNSAFE.compareAndSwapLong(this, stateOffset,
s, s + adjust))
break;
}
}
else if (parent == null) { // 1st root registration
// 第一次注册,在当前的state上加上新的parties值
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0000, 0000 0000 0000 0000 # (long)phase << PHASE_SHIFT)
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # adjust
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0001, 0000 0000 0000 0001 # 与运算后新的state
long next = ((long)phase << PHASE_SHIFT) | adjust;
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
break;
}
else {
// 多层级阶段的处理方式,本文暂不考虑
synchronized (this) { // 1st sub registration
if (state == s) { // recheck under lock
phase = parent.doRegister(1);
if (phase < 0)
break;
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
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;
}
看到这里,这个方法的处理不是太复杂,流程如下:
- 增加一个参与者,需要同时增加parties和unarrived两个数值,也就是state的中16位和低16位;
- 如果是第一个参与者,则尝试原子更新state的值,如果成功了就退出;
- 如果不是第一个参与者,则检查是不是在执行onAdvance(),如果是等待onAdvance()执行完成,如果否则尝试原子更新state的值,直到成功退出;
- 等待onAdvance()完成是采用先自旋后进入队列排队的方式等待,减少线程上下文切换;(参考下面internalAwaitAdvance方法讲解)
internalAwaitAdvance()
doRegister()和arriveAndAwaitAdvance()方法都调用这个方法处理内部等待phase递增操作。
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;
long s;
int p;
// 检查当前阶段是否变化,如果变化了说明进入下一个阶段了,这时候就没有必要自旋了
// (s = state) >>> PHASE_SHIFT state先无符号右移32位,得到(bits 32-62),在cast为int,得到当前的phase值
while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {
// 如果node为空,注册的时候传入的为空
if (node == null) { // spinning in noninterruptible mode
int unarrived = (int)s & UNARRIVED_MASK;
// unarrived有变化,增加自旋次数
if (unarrived != lastUnarrived &&
(lastUnarrived = unarrived) < NCPU)
spins += SPINS_PER_ARRIVAL;
boolean interrupted = Thread.interrupted();
// 自旋次数完了,则新建一个节点
if (interrupted || --spins < 0) { // need node to record intr
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;
QNode q = node.next = head.get();
// 确保当前仍是改phase,即phase值未变
if ((q == null || q.phase == phase) &&
(int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
queued = head.compareAndSet(q, node);
}
else {
// 当前线程进入阻塞状态,跟调用LockSupport.park()一样,等待被唤醒
try {
ForkJoinPool.managedBlock(node);
} catch (InterruptedException ie) {
node.wasInterrupted = true;
}
}
}
// 到这里说明节点所在线程已经被唤醒了
if (node != null) {
// 置空节点中的线程
if (node.thread != null)
node.thread = null; // avoid need for unpark()
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;
}
改方法会等待onAdvance()方法执行完毕,原理是先自旋一定次数,如果进入下一个阶段,这个方法直接就返回了,如果自旋一定次数后还没有进入下一个阶段,则当前线程入队列,等待onAdvance()执行完毕唤醒。
arriveAndAwaitAdvance()
通过刚才的new操作和register操作,假设当前已有2个parties。
public int arriveAndAwaitAdvance() {
// Specialization of doArrive+awaitAdvance eliminating some reads/paths
final Phaser root = this.root;
for (;;) {
// 暂不考虑parent的情况,s即为state
long s = (root == this) ? state : reconcileState();
// 和doRegister()一样,通过state获取phase、unarrived
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));
// 由于ONE_ARRIVAL为1,在低16位上表示unarrived,所以s -= ONE_ARRIVAL就可以直接表示Phaser的unarrived减1的状态
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s -= ONE_ARRIVAL)) {
// 如果不是最后一个到达的,则调用internalAwaitAdvance()方法自旋或进入队列等待
if (unarrived > 1)
// // 这里是直接返回了,internalAwaitAdvance()方法的源码见register()方法解析
return root.internalAwaitAdvance(phase, null);
if (root != this)
return parent.arriveAndAwaitAdvance();
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0010, 0000 0000 0000 0001 # s
// 0,000 0000 0000 0000 0000 0000 0000 0000, 1111 1111 1111 1111, 0000 0000 0000 0000 # PARTIES_MASK
// 0,000 0000 0000 0000 0000 0000 0000 0000, 0000 0000 0000 0010, 0000 0000 0000 0000 # n(高32位全为0,方便设置phase)
long n = s & PARTIES_MASK; // base of next state
// parties的值,即下一次需要到达的参与者数量
// 0000 0000 0000 0000, 0000 0000 0000 0010 # 先cast,再无符号右移16位
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
// 执行onAdvance()方法,返回true表示下一阶段参与者数量为0了,也就是结束了
if (onAdvance(phase, nextUnarrived))
n |= TERMINATION_BIT;
else if (nextUnarrived == 0)
n |= EMPTY;
else
// n 加上unarrived的值,即低32位已拼装好
n |= nextUnarrived;
// 下一个阶段等待当前阶段加1
int nextPhase = (phase + 1) & MAX_PHASE;
// 低32位与高(bits 32-62)做与运算,即拼装phase的值到state中去
n |= (long)nextPhase << PHASE_SHIFT;
// 修改state的值为n
if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
return (int)(state >>> PHASE_SHIFT); // terminated
// 唤醒其它参与者并进入下一个阶段
releaseWaiters(phase);
// 返回下一阶段的值
return nextPhase;
}
}
}
看到最后,这个方法的作用就是递减unarrived数量,如果还不是最后一个则等待其他parties,一直到最后一个unarrived被处理,phase加1并开启新一轮任务。
其他方法
-
arrive():到达,阻塞,等到当前phase下其他parties到达。如果没有register(即已register数量为0),调用此方法将会抛出异常,此方法返回当前phase周期数,如果Phaser已经终止,则返回负数。
-
arriveAndDeregister():到达,并注销一个parties数量,非阻塞方法。注销,将会导致Phaser内部的parties个数减一(只影响当前phase),即下一个phase需要等待arrive的parties数量将减一。异常机制和返回值,与arrive方法一致。
-
awaitAdvance(int phase):阻塞方法,等待phase周期数下其他所有的parties都到达。如果指定的phase与Phaser当前的phase不一致,则立即返回。
-
awaitAdvanceInterruptibly(int phase):阻塞方法,同awaitAdvance,只是支持interrupted响应,即waiter线程如果被外部中断,则此方法立即返回,并抛出InterrutedException。
-
awaitAdvanceInterruptibly(int phase,long timeout,TimeUnit unit):阻塞方法,同awaitAdvance,支持timeout类型的interrupted响应,即当前线程阻塞等待约定的时长,超时后以TimeoutException异常方式返回。
-
forceTermination():强制终止,此后Phaser对象将不可用,即register等将不再有效。此方法将会导致Queue中所有的waiter线程被唤醒。
-
bulkRegister(int parties):批量注册多个parties数组。
-
onAdvance(int phase,int registeredParties):这个方法比较特殊,表示当进入下一个phase时可以进行的事件处理,如果返回true表示此Phaser应该终止(此后将会把Phaser的状态为termination,即isTermination()将返回true),否则可以继续进行。phase参数表示当前周期数,registeredParties表示当前已经注册的parties个数。
默认实现为:
return registeredParties == 0;
在很多情况下,开发者可以通过重写此方法,来实现自定义的advance时间处理机制。
Phaser相对于CyclicBarrier和CountDownLatch的优势
- Phaser可以完成多阶段,而一个CyclicBarrier或者CountDownLatch一般只能控制一到两个阶段的任务;
- Phaser每个阶段的任务数量可以控制,而一个CyclicBarrier或者CountDownLatch任务数量一旦确定不可修改。
写在最后
Phaser的使用与理解花了我很多时间,其中很多实现手法可以让研发人员细细琢磨,回味无穷,希望我的这些理解能帮到你。
最后若发现写的不对的地方,希望能在评论区留下意见,万分感激。