JUC源码分析-辅助类-Phaser

概述

Phaser 是一个同步辅助类,与CyclicBarrier和CountDownLatch功能相似,用于栅栏条件的相互等待,支持更丰富的用法, 可以将一个的程序的等待分为多个阶段。

主状态分为 4部分,1~16位 未到达者数,17~32位参与数 parties,33~62 阶段值 phase, 63位 结束标识, 64位正负标识

 

private volatile long state;

//evenQ,oddQ都是里面放Node节点的CAS工具类,方便执行CAS 替换Node节点

//evenQ,oddQ中放的都是头结点,节点构成一个单项列表, 在头部插入节点。

private final AtomicReference<QNode> evenQ;

private final AtomicReference<QNode> oddQ;

 

源码分析

Phaser构造方法

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;

this.root = root;//父节点的root就是当前的root

this.evenQ = root.evenQ;//使用父节点的队列容器

this.oddQ = root.oddQ;//同上

if (parties != 0)//如果参与者数不为0

phase = parent.doRegister(1);//父类注册一个参与者。

}

else {//没有父节点 初始化 root, evenQ,oddQ

this.root = this;

this.evenQ = new AtomicReference<QNode>();

this.oddQ = new AtomicReference<QNode>();

}//获取主状态值

this.state = (parties == 0) ? (long)EMPTY :

((long)phase << PHASE_SHIFT) |

((long)parties << PARTIES_SHIFT) |

((long)parties);

}

如果没有父节点 root==this,evenQ,oddQ都是新创建的。

 

分析一下Node

构造方法

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.lastTime = timed ? System.nanoTime() : 0L;

thread = Thread.currentThread();

}

 

存放了phaser,阶段值phase,是否可打断, 超时时间,创建线程。

 

public boolean isReleasable() {

if (thread == null)

return true;

if (phaser.getPhase() != phase) {

thread = null;

return true;

}

if (Thread.interrupted())

wasInterrupted = true;

if (wasInterrupted && interruptible) {

thread = null;

return true;

}

if (timed) {

if (nanos > 0L) {

long now = System.nanoTime();

nanos -= now - lastTime;

lastTime = now;

}

if (nanos <= 0L) {

thread = null;

return true;

}

}

return false;

}

在thread为空,phaser的阶段值与当前阶段值phase不同,被打断,超时返回ture,否则返回false

 

public boolean block() {

if (isReleasable())

return true;

else if (!timed)

LockSupport.park(this);

else if (nanos > 0)

LockSupport.parkNanos(this, nanos);

return isReleasable();

}

block是阻塞方法。

 

register分析

一个线程加入Phaser活动,要先执行register。

public int register() {

return doRegister(1);

}

private int doRegister(int registrations) {

// adjustment to state

long adj = ((long)registrations << PARTIES_SHIFT) | registrations;

final Phaser parent = this.parent;

int phase;

for (;;) {

long s = state;

int counts = (int)s;

int parties = counts >>> PARTIES_SHIFT;

int unarrived = counts & UNARRIVED_MASK;

if (registrations > MAX_PARTIES - parties)//超出限制注册数

throw new IllegalStateException(badRegister(s));

else if ((phase = (int)(s >>> PHASE_SHIFT)) < 0)//阶段值出现负数,重新执行

break;

else if (counts != EMPTY) { //不是第一次注册,将参与者和位到达者次数加1

if (parent == null || reconcileState() == s) {//reconcileState后面详解。

if (unarrived == 0) //当前未到达数等于0,说明进入了下个节点,root等待进入下个阶段。

root.internalAwaitAdvance(phase, null);

else if (UNSAFE.compareAndSwapLong(this, stateOffset,

s, s + adj))//更新主状态

break;

}

}

else if (parent == null) {//没有父Phaser, 第一次注册,初始化状态值,将参与者和位到达者次数加1

long next = ((long)phase << PHASE_SHIFT) | adj;//组合初次注册状态值

if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))//更新主状态

break;

}

else {//有父Phaser

synchronized (this) { // 第一次子节点注册

if (state == s) { // 重新检查状态

parent.doRegister(1);//先注册父节点

do { // 当前的阶段值从父节点获取。

phase = (int)(root.state >>> PHASE_SHIFT);

// assert phase < 0 || (int)state == EMPTY;

} while (!UNSAFE.compareAndSwapLong //更新主状态值

(this, stateOffset, state,

((long)phase << PHASE_SHIFT) | adj));

break;

}

}

}

}

return phase;

}

代码看似复杂,实际主要是将参与者和未到达者次数加1.

 

doRegister方法中会调用reconcileState来调整状态,看下这个方法实现:

private long reconcileState() {

final Phaser root = this.root;

long s = state;

if (root != this) {

int phase, u, p;

// CAS root phase with current parties; possibly trip unarrived

while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=

(int)(s >>> PHASE_SHIFT) &&

!UNSAFE.compareAndSwapLong

(this, stateOffset, s,

s = (((long)phase << PHASE_SHIFT) |

(s & PARTIES_MASK) |

((p = (int)s >>> PARTIES_SHIFT) == 0 ? EMPTY :

(u = (int)s & UNARRIVED_MASK) == 0 ? p : u))))

s = state;

}

return s;

}

这个方法要做的事情就是将当前Phaser和root Phaser的phase值调整为一致的。

 

arriveAndAwaitAdvance分析

到达并等待全员到齐

参与者只要有一个没执行这个方法,就阻塞,直到到达栅栏条件。

public int arriveAndAwaitAdvance() {

// Specialization of doArrive+awaitAdvance eliminating some reads/paths

final Phaser root = this.root;

for (;;) {

long s = (root == this) ? state : reconcileState();

int phase = (int)(s >>> PHASE_SHIFT);

int counts = (int)s;

int unarrived = (counts & UNARRIVED_MASK) - 1;

if (phase < 0)

return phase;

else if (counts == EMPTY || unarrived < 0) {//异常校验

if (reconcileState() == s)

throw new IllegalStateException(badArrive(s));

}

else if (UNSAFE.compareAndSwapLong(this, stateOffset, s,

s -= ONE_ARRIVAL)) {将参与者和未到达者次数减1

if (unarrived != 0)//还有未到达者,执行等待。

return root.internalAwaitAdvance(phase, null);

if (root != this)// unarrived ==0 但root不是当前对象,执行父对象等待

return parent.arriveAndAwaitAdvance();

//unarrived ==0 组装下阶段的状态值 并释放所有阻塞的节点

long n = s & PARTIES_MASK; //获取下个状态值得基本部分 这部分是只包含参与者数 其他位数数值为0

int nextUnarrived = (int)n >>> PARTIES_SHIFT;//获取下阶段未参与者数

if (onAdvance(phase, nextUnarrived))// 轮数增长钩子方法。

n |= TERMINATION_BIT;

else if (nextUnarrived == 0)

n |= EMPTY;

else

n |= nextUnarrived;//合并未参与者数

int nextPhase = (phase + 1) & MAX_PHASE;//获取下阶段数值(初始值0)

n |= (long)nextPhase << PHASE_SHIFT;//合并阶段数

if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))//更新状态

return (int)(state >>> PHASE_SHIFT); // 状态修改失败,终止,返回阶段数

releaseWaiters(phase);//释放所有参与者

return nextPhase;//返回下阶段值

}

}

}

逻辑总结:

  1. 将参与者和未到达者次数减1
  2. 如果未到达者数不等于0,执行等待,等待结束后返回阶段值。
  3. 如果未到达者数等于0
    1. 如果当前是子Phaser,执行父Phaser的等待,等待结束后返回阶段值。
    2. 取出参与者数作为下阶段的位到达数,阶段值加1,组装下阶段状态值, 并释放所有阻塞的节点

internalAwaitAdvance分析

自旋,入队节点,阻塞等待全员到达开始下个阶段

private int internalAwaitAdvance(int phase, QNode node) {

releaseWaiters(phase-1); //确保旧的队列 干净

boolean queued = false; // 当节点入队后改为 true

int lastUnarrived = 0; // to increase spins upon change

int spins = SPINS_PER_ARRIVAL;

long s;

int p;

while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {//阶段值没发生变化 ,一直循环

if (node == null) { // spinning in noninterruptible mode

int unarrived = (int)s & UNARRIVED_MASK;

if (unarrived != lastUnarrived &&//当第一次执行 或 未到达数发生变化 且 未到达数小于CPU数时增加 自旋次数

(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) { // node 入队

AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;//获取当前队列,两个队列交换使用

QNode q = node.next = head.get();//获取头节点 并将node next指向头节点。 说明 node入队后放在头部

if ((q == null || q.phase == phase) &&//头节点是空的 或头结点阶段值等于当前阶段值

(int)(state >>> PHASE_SHIFT) == phase) //检查阶段值是否更改

queued = head.compareAndSet(q, node);//更新头节点

}

else {

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;

}

逻辑总结:

  1. 自旋,并根据未到达数和CPU数调整自旋次数
  2. 记录打断状态,创建不可打断不可超时节点,
  3. 如果可以释放,终止调用。否则入队 阻塞,再次检查是否可以释放, 不可释放 进入阻塞等待唤醒。
  4. 被唤醒 出队并 释放所有等待节点。

 

总结:Phaser 中long类型的状态值 不同位数 记录 未到达数,参与者数,阶段数,终止标识。执行过程包括 注册,到达,等待阶段增长 , 注册 将未到达数和参与者数1,到达将未到达数和参与者数1。 等待阶段增长 先到达的 自旋,入队等待队列,阻塞等待唤醒。 最后到达的初始化下轮状态值,唤醒等待队列中所有节点。arriveAndAwaitAdvance合并了 到达和等待阶段增长。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值