JUC并发工具包之Phaser Part2 源码分析

系列文章

JUC并发工具包之Phaser Part1 结构与状态
JUC并发工具包之Phaser Part2 源码分析
JUC并发工具包之Phaser Part3 场景使用
JUC并发工具包之Semaphore
JUC并发工具包之CyclicBarrier
JUC并发工具包之CountDownLatch
JUC并发工具包之CyclicBarrier & CountDownLatch的异同

预备知识

读Phaser的源码很大的一个特点就是位运算太多,还有就是QNode使用的是Treiber Stack,建议先看下另外几篇文章,会很有帮助。

主要方法

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的使用与理解花了我很多时间,其中很多实现手法可以让研发人员细细琢磨,回味无穷,希望我的这些理解能帮到你。
最后若发现写的不对的地方,希望能在评论区留下意见,万分感激。

猜你喜欢

转载自blog.csdn.net/HalfImmortal/article/details/107169767