Phaser案例及源码分析

---
name: Phaser源码解析
title: Phaser源码解析
tags: Phaser源码解析
categories:JUC系列
date: 2023-1-22
---

参数

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;      // 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;

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();
}

例子1:

通过Phaser控制多个线程的执行时机:有时候我们希望所有线程到达指定点后再同时开始执行

public class PhaserTest1 {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(); //注意这里是0
        for (int i = 0; i < 10; i++) {
            phaser.register();                  // 注册各个参与者线程
       new Thread(()->{
           if (Thread.currentThread().getName().equals("Thread-1")) {
               try {
                   System.out.println(Thread.currentThread().getName() + "我有点事情,等我一下");
                   Thread.sleep(5000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           System.out.println(Thread.currentThread().getName() + "开始执行了·····");
           int i = phaser.arriveAndAwaitAdvance();     // 等待其它参与者线程到达
           // do something
           System.out.println(Thread.currentThread().getName() + ": 执行完任务,当前phase =" + i + "");
       }, "Thread-" + i).start();
        }
    }
}


例子2:

通过Phaser实现开关。在以前讲CountDownLatch时,我们给出过以CountDownLatch实现开关的示例,也就是说,我们希望一些外部条件得到满足后,然后打开开关,线程才能继续执行,我们看下如何用Phaser来实现此功能。

开关

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser(1);       // 注册主线程,当外部条件满足时,由主线程打开开关
        for (int i = 0; i < 10; i++) {
            phaser.register();                      // 注册各个参与者线程
            new Thread(() -> {

                System.out.println(Thread.currentThread().getName() + "开始执行了·····");
                Thread.sleep(100);//等待让线程先register()
                int ret = phaser.arriveAndAwaitAdvance();     // 等待其它参与者线程到达
                // do something
                System.out.println(Thread.currentThread().getName() + ": 执行完任务,当前phase =" + ret + "");
            }, "Thread-" + i).start();
        }

        System.out.println("等待主线程打开开关·····");
        // 外部条件:等待用户输入命令
        Thread.sleep(3000);
        // 打开开关
        phaser.arriveAndDeregister();
        System.out.println("主线程打开了开关");
    }
}

可以看出例子2和例子1的区别在于:**Phaser()的构造参数和arriveAndDeregister()**函数:

源码分析:

·Phaser构造

public Phaser(Phaser parent, int parties) {
    //...异常校验
    int phase = 0;
    this.parent = parent;
    //parent不为null的情况下
    else {
        this.root = this;
        this.evenQ = new AtomicReference<QNode>();
        this.oddQ = new AtomicReference<QNode>();
    }
    //重点是这里:当parties为1时,state为多少呢?
    this.state = (parties == 0) ? (long)EMPTY :
    ((long)phase << PHASE_SHIFT) |
        ((long)parties << PARTIES_SHIFT) |
        ((long)parties);
}

//------
(long)parties=1=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
(long)phase=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
    
(long)phase << 32==0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
(long)parties<<16==0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000
(long)parties    ==0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
将上面三个做或运算  ==0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001 也就是state=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001 

·register()

private int doRegister(int registrations) {  
    // adjustment to state
    long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
    final Phaser parent = this.parent;
    int phase;
    for (;;) {
        long s = (parent == null) ? state : reconcileState();
        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) {                  // not 1st registration
            if (parent == null || reconcileState() == s) {
                if (unarrived == 0)             // wait out advance
                    root.internalAwaitAdvance(phase, null);
                else if (UNSAFE.compareAndSwapLong(this, stateOffset,
                                                   s, s + adjust))
                    break;
            }
        }
        else if (parent == null) {              // 1st root registration
            long next = ((long)phase << PHASE_SHIFT) | adjust;
            if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))
                break;
        }
        else {
        //parent 不为空
        }
    }
    return phase;
}
前提:new Phser(1),此时看第一个线程进来会做些什么事情
registrations=1=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
(long)registrations << PARTIES_SHIFT=16
 ==0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 
或上0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
 ==0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001
 ==也就是将参与线程数和未到达线程数都加1==adjust
 s=state=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001(基于第一步)
 counts=(int)s,这相当于直接取低32位=0000 0000 0000 0001 0000 0000 0000 0001
 parties = counts >>>16 = 0000 0000 0000 0000 0000 0000 0000 0001 取中间16位(参加的线程数)
 unarrived = counts & 0x0000 ffff=0000 0000 0000 0000 0000 0000 0000 0001 取低16位(未达到的线程数)
 phase = (int)(s >>> 32)=0000 0000 0000 0000 0000 0000 0000 0000 = 0
 由于此时不是第一次进来注册了,因为在new Phaser(1)的时候就已经注册了一个线程,也就是主线程,所以if (counts!= 	  EMPTY)为true,所以走这个判断,然后判断未达到的线程数是否为0(如果为0,那么代表该阶段的所需要的线程已经满足,此时当前线程应该取阻塞),显然由上面的计算结果得知unarrived=1(也就是主线程注册的),所以不走这里,而是走下面的CAS操作,
 将s的值加上adjust,也就是:
 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001 adjust
 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001
=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0010
然后break;
此时结果为要参与的线程数为2,未达到的线程数也为2

//总结register()的作用:
1.判断是否是第一次进来注册
	如果是:直接CAS将state变量中参与线程数和未达到线程数都+1(低32位中的高16位和低16位)
	如果不是:判断未到达线程数是否为0:
		如果为0:阻塞(这里不准确)
		如果不为0:直接CAS将state变量中参与线程数和未达到线程数都+1(低32位中的高16位和低16位)

·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);
        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)) {
            if (unarrived > 1)
                return root.internalAwaitAdvance(phase, null);
            if (root != this)
                return parent.arriveAndAwaitAdvance();
            long n = s & PARTIES_MASK;  // base of next state
            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;
            n |= (long)nextPhase << PHASE_SHIFT;
            if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))
                return (int)(state >>> PHASE_SHIFT); // terminated
            releaseWaiters(phase);
            return nextPhase;
        }
    }
}
前提:state为上一步register()的结果
     state=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0010
//注意是在死循环里面的操作
for(;;){
s=state=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0010
phase=0000 0000 0000 0000 0000 0000 0000 0000=0
counts=s的低32位=0000 0000 0000 0010 0000 0000 0000 0010
unarrived=低32位中的低16位=0000 0000 0000 0010=2
CAS将s的值减去1(ONE_ARRIVAL),注意Doug lea的操作:CAS(,,,s -= ONE_ARRIVAL),那么下面操作s的值就是新值
此时s=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0001
也就是将未到达线程数-1{ //CAS成功继续里面的逻辑
判断未到达的线程数是否 > 1 :若是则去阻塞(此处不严谨)
显然这里的unarrived=2 > 1 所以应该去阻塞

总结:
先保存未到达线程数
然后会将未到达线程数-1,
再判断已保存未到达线程数是否 > 1 
	若是: 则去阻塞(不严谨)
	若不是:则往下操作(这里先不看)
    
//
也就是说:
只要未到达线程数 > 1 ,也就是说线程没有全部到齐,那么就应该去阻塞去

//
因为我们new Phaser(1)的时候手动注册了一个主线程进去,
所以后面的线程调完register()后再调arriveAndAwaitAdvance()会将参与线程数+1,然后未到达线程数-1
此处是第一个线程调用,后面还有9个线程会调用register()后再调arriveAndAwaitAdvance(),
那么最终结果会是:
s=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1011 0000 0000 0000 0001
也就是参与线程数为11,但是未到达线程数为1(也就是主线程),
这样就达到了我们想要的结果:就是需要等待主线程发号后,其余线程才能继续执行的效果(抽象为一组线程需要等待某个条件完成后才能继续执行)
那么接下来看看主线程是如何发号的?

·arriveAndDeregister()

public int arriveAndDeregister() {
    return doArrive(ONE_DEREGISTER);
}
private int doArrive(int adjust) { //adjust=ONE_DEREGISTER(ONE_ARRIVAL|ONE_PARTY)
    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)) {
            if (unarrived == 1) {
                long n = s & PARTIES_MASK;  // base of next state
                int nextUnarrived = (int)n >>> PARTIES_SHIFT;
                if (root == this) {
                    if (onAdvance(phase, nextUnarrived))
                        n |= TERMINATION_BIT;
                    else if (nextUnarrived == 0)
                        n |= EMPTY;
                    else
                        n |= nextUnarrived;
                    int nextPhase = (phase + 1) & MAX_PHASE;
                    n |= (long)nextPhase << PHASE_SHIFT;
                    UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
                    releaseWaiters(phase);
                }
                else if (nextUnarrived == 0) { // propagate deregistration
                    phase = parent.doArrive(ONE_DEREGISTER);
                    UNSAFE.compareAndSwapLong(this, stateOffset,
                                              s, s | EMPTY);
                }
                else
                    phase = parent.doArrive(ONE_ARRIVAL);
            }
            return phase;
        }
    }
前提:state=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1011 0000 0000 0000 0001
adjust=ONE_DEREGISTER(ONE_ARRIVAL|ONE_PARTY)
=1 | 1 << 16 
 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000
=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0001
s=state=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1011 0000 0000 0000 0001
phase=(int)(s>>>32)=0000 0000 0000 0000 0000 0000 0000 0000=0
counts=s的低32位=0000 0000 0000 1011 0000 0000 0000 0001
unarrived=counts&0xffff=低16位=0000 0000 0000 0001=1
CAS将s-=adjust,将参与线程数和未到达线程数都减1
此时s=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1010 0000 0000 0000 0000
判断上面的unarrived是否等于1:这里是等于1
n=s&0xffff0000L=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1010 0000 0000 0000 0000
nextUnarrived=(int)n >>> 16=0000 0000 0000 0000 0000 0000 0000 1010
n |= nextUnarrived:
n=	0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1010 0000 0000 0000 0000
											0000 0000 0000 0000 0000 0000 0000 1010
 =  0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1010 0000 0000 0000 1010
nextPhase=(phase + 1) & MAX_PHASE=1
n |= (long)nextPhase << 32
= 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 1010 0000 0000 0000 1010
CAS将state的值设置为n
然后执行releaseWaiters(phase)来唤醒队列阻塞的中的所有线程

也就意味着当主线程充当了发号的功能

将断点打在phaser.arriveAndDeregister();这里然后DEBUG验证确实如分析所示:

state=1011 0000 0000 0000 0001和上面分析一样
在这里插入图片描述

当这段代码执行完毕后:

state变为了:0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 1010 0000 0000 0000 1010

并且阻塞队列中元素都被唤醒了

并且参与线程数减1

在这里插入图片描述

例子3:

通过Phaser控制任务的执行轮数:

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        int repeats = 3;    // 指定任务最多执行的次数
        Phaser phaser = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("---------------PHASE[" + phase + "],Parties[" + registeredParties + "] ---------------");
                //阶段数是从0开始的
                return phase + 1 >= repeats || registeredParties == 0;
            }
        };

        for (int i = 0; i < 10; i++) {
            phaser.register();                      // 注册各个参与者线程
            new Thread(() -> {
                try {
                    Thread.sleep(500);
                    while (!phaser.isTerminated()) {   //只要Phaser没有终止, 各个线程的任务就会一直执行
                        int ret = phaser.arriveAndAwaitAdvance();     // 等待其它参与者线程到达
                        // do something
                        System.out.println(Thread.currentThread().getName() + ": 执行完任务");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }, "Thread-" + i).start();
        }
    }

·源码分析:

可以看到这里和例子1的主要区别是在new Phaser()的时候传入了一个重写方法onAdvance()
而这个方法在register()和arriveAndAwaitAdvance()和arriveAndDeregister()中都会调用
如果重写了该方法,那么执行的逻辑就是我们的判断逻辑:而我们的逻辑是判断阶段数是否为>3,如果大于3则返回true(终止执行),
否则返回false继续执行。这部分逻辑比较简单,就不过多分析了。

例子4:

Phaser支持分层功能,我们先来考虑下如何用利用Phaser的分层来实现高并发时的优化,在示例三中,我们其实创建了10个任务,然后10个线程共用一个Phaser对象,如下图:

在这里插入图片描述

如果任务数继续增大,那么同步产生的开销会非常大,利用Phaser分层的功能,我们可以限定每个Phaser对象的最大使用线程(任务数),如下图:

在这里插入图片描述

·代码

public class PhaserTreeTest {
    //  
    private static final int TASKS_PER_PHASER = 4; //每个子phaser执行4个任务

    public static void main(String args[]) throws Exception {
        //  
        final int phaseToTerminate = 3;//阶段数
        final Phaser phaser = new Phaser() {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("====== " + phase + " ======");
                return phase == phaseToTerminate || registeredParties == 0;
            }
        };

        //  
        final Task tasks[] = new Task[10];//执行任务的那10个线程
        build(tasks, 0, 10, phaser);//构建层级关系
        
        for (int i = 0; i < 10; i++) {
            System.out.println("starting thread, id: " + i);
            final Thread thread = new Thread(tasks[i]);
            thread.start();
        }
    }

    /*
        总的参与者个数应该是 n + (int) Math.ceil( n/TASKS_PER_PHASER ),
        总的参与者个数应该是 10 + (int) Math.ceil( 10/4 ),12
        因为每一个子Phaser也会作为一个参与者注册到父Phaser中
     */
    public static void build(Task[] tasks, int lo, int hi, Phaser ph) {
        if (hi - lo > 4) {
            for (int i = lo; i < hi; i += 4) {
                int j = Math.min(i + 4, hi);
                build(tasks, i, j, new Phaser(ph));
            }
        } else {
            for (int i = lo; i < hi; ++i)
                tasks[i] = new Task(i, ph);
        }
    }

    public static class Task implements Runnable {
        //  
        private final int id;
        private final Phaser phaser;

        public Task(int id, Phaser phaser) {
            this.id = id;
            this.phaser = phaser;
            this.phaser.register();
        }

        @Override
        public void run() {
            while (!phaser.isTerminated()) {
                System.out.println(Thread.currentThread().getName() + "begin·····");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // NOP  
                }
                System.out.println("in Task.run(), phase: " + phaser.getPhase() + ", id: " + this.id);
                phaser.arriveAndAwaitAdvance();
            }
        }
    }
}

·源码分析:

首先需要知道当上述代码执行到19行的时候(也就是第18行执行完了),各个phaser的关系是怎么样的?图如下:

每个Phaser所对应的数字是我IDEA中DEBUG时我记录的每个对象的地址,用来区分不同的phaser

在这里插入图片描述

并且需要注意:如果在new Phaser()的时候传入的parent,那么子phaser会使用父phaser的root:图如下:

可以看到parent为null,这就说明了是父phaser,因为子phaser是有parent的,并且parties=3,这是为什么呢?

是因为每个子phaser分到4个任务,那么10个任务就需要3个子phaser来完成,最后一个子phaser分到2个任务
各个phaser的状态:可以从task[i]找到各个phaser的状态:

·第一个子pahser:task[0-3]

在这里插入图片描述

·第二个子phaser:task[4-7]

在这里插入图片描述

·第3个子phaser:task[8-9]

在这里插入图片描述

可以看到:第一个子pahser和第二个子phaser的state变量为:262148,而第三个子phaser的state值为:131074,这是为什么?

将其转为二进制就很容易明白:

262148=0100 0000 0000 0000 0100

131074=0010 0000 0000 0000 0010

因为前2个子phaser被分配到的任务为4,所以参加线程数和未到达线程数都是4:0100

而第3个子phaser被分配到2个任务,所以参加线程数和未到达线程数是2:0010

接下来就是开始10个线程的代码了,接下来分析父子phaser之间是如何协作的:

0-3号的task在创建的时候会调用phaser.register(),
而这个方法的调用会将state变量中的参与线程数和未到达线程数都+1,
因为当前phaser(c63)拥有4个task,所以会调用4次register(),
会加4次,也就是说中16位和低16位都是为0100
所以state=0100 0000 0000 0000 0100

同理4-7号的task的ph的state同上

8-9号的task的ph的state应该为:
0010 0000 0000 0000 0010

接下来开始创建线程:
当调用start的时候会调用到run(),也就是调用 phaser.arriveAndAwaitAdvance():
接下来看看当有parent节点时是如何进行协调的。

注意此时是第一个线程进来操作:并且是phaser(c63)的task[0]
此时state=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0100

注意:this是task[0]的phaser,
而phaser.root是parent的root,
然后进行判断:this是否等于root,
很明显这里不相等,
所以会执行reconcileState():协调State

先拿到parent的root
然后将当前phaser的state赋值给s
因为现在是task[0]-->所对应的phaser的state=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0100
如果root!=当前phaser,那么进入循环体:Doug Lea写代码的风格来了,能简单就简单。

(phase = (int)(root.state >>> PHASE_SHIFT)) 
root.state-->是parent的state=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0011
右移32位得到父root的阶段数,这里是0 
将当前phaser的state右移32位获得当前phaser的阶段数,这里也是0
所以判断为false,所以while()循环退出,执行s=state(当前phaser的state),s=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0100
将s返回,执行for(;;),注意:接下来的s是0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0100,也就是当前phaser的state
phase=s右移32位=0
counts=s的低32位=0000 0000 0000 0100 0000 0000 0000 0100
保存当前的unarrived=counts的低16位=0000 0000 0000 0100=4
CAS并且更新s的值为s-1=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0011
然后去阻塞,
同理task[1],task[2]执行到这里的时候也是去阻塞,
当task[3]执行到这里的时候:也是先执行reconcileState,由于阶段数没有改变所以还是操作当前phaser的state,因为task 0-2已经执行过了,所以此时的state=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0001
phase=0
counts=s的低32位=0000 0000 0000 0100 0000 0000 0000 0001
unarrived=counts的低16位=0000 0000 0000 0001
CAS并且更新s的值为s-1=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0000
此时进行判断if(unarrived > 1)显然为false,然后判断root是否等于当前phaser,
显然这里为true,意味着当前phaser有parent,
则调用parent的arriveAndAwaitAdvance():再次进入该方法:注意此时执行的是parent的
拿到root=this.root,这里的this就是parent
然后判断root是否等于this,很明显,这里是等于的,
所以直接返回parent的state=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0011
然后获取parent的phase=0
counts=s的低32位=0000 0000 0000 0011 0000 0000 0000 0011
保存unarrived=counts的低16位=0000 0000 0000 0011=3
CAS并且更新s的值为s-1=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0010
对于parent来说,有3个子phaser,所以需要等待这3个子phaser都完成后才能执行,所以这里第一个phaser需要去阻塞,
从这里可以看出当子phaser准备好后需要通知parent

然后继续执行task[4]-task[7]的线程,很明显,
这4个task[]的操作和task[0--3]的操作一样,也是先将自己的state修改到0后,执行parent的awaitAndAwaitAdvacne()方法,
然后将parent的state修改为0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0001

当执行task[8-9]时,前面都和上面的一样,当task[8]去阻塞后,task[9]执行的时候,因为它是最后一个线程了,
所以unarrived=1,并不会执行阻塞操作,而是去执行parent的arriveAndAwaitAdvance(),
进入该方法,
获取parent的state=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0001
phase=0
counts=低32位=0000 0000 0000 0011 0000 0000 0000 0001
保存unarrived=低16位=1
CAS并且更新state的值为s-1=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0000
此时因为是最后一个线程了,所以不会执行阻塞操作,并且此时this==root,所以这意味着是当前阶段的最后一个线程了,
所以接下来的操作是为下一阶段做一些准备工作,
n=s & 0xffff 0000 也就是取中间16位=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0000
nextUnarrived=n的低32位右移16位=0000 0000 0000 0000 0000 0000 0000 0011
然后判断是否需要终止,因为还没有到第3个阶段,所以不需要终止。
n|=nextUnarrived=0000 0000 0000 0000 0000 0000 0000 0000   0000 0000 0000 0011 0000 0000 0000 0011
nextPhase=0+1=1
n|=(long)phase <<< 32=0000 0000 0000 0000 0000 0000 0000 0001   0000 0000 0000 0011 0000 0000 0000 0011
然后CAS修改当前phaser(这里指的是parent)的state为n
然后唤醒阻塞的线程(也就是前面9个被阻塞的线程)
然后返回下一个阶段数

再来分析一下第2阶段执行时:task[0]的关键点(也就是reconcileState()的执行)
当task[0]进来的时候,也会执行reconcileState(),
注意当前phaser的state=
注意这个时候就和它第一次进来的时候不同了,因为此时当前phaser的阶段数和parent的阶段数不同了,
所以while()的第一个判断会为true,然后继续看第二个判断:CAS(xxxx)
((long)phase << PHASE_SHIFT)
将parent的阶段数1左移32位=0000 0000 0000 0000 0000 0000 0000 0001 0000 0000 0000 0000 0000 0000 0000 0000  

((phase < 0) ? (s & COUNTS_MASK) :
  (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
 ((s & PARTIES_MASK) | p)))     
看看这段代码表达什么意思:
1.判断parent的阶段数是否小于0,很显然这里几乎不可能
所以会执行后面的代码:
 (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
 ((s & PARTIES_MASK) | p))
1.判断当前阶段的参与线程数是否为0,如果为0,则变为EMPTY,显然这里也不会0(思考为什么会考虑这样的代码?应该是因为当前阶段可能每次都调用arriveAndDeregister()将参与线程数-1)
但是我们不考虑这种情况
2.将s(当前state) & 0xffff 0000(也就是取中间16位)=0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0100 & 0xffff 0000
    =0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0000
再和当前阶段的参与线程数相与 得到:
0000 0000 0000 0000 0000 0000 0000 0000    0000 0000 0000 0100 0000 0000 0000 0100
再和上面的parent的阶段数相与,最终得到:
s=0000 0000 0000 0000 0000 0000 0000 0001    0000 0000 0000 0100 0000 0000 0000 0100
返回给子phaser使用

总结一下:层级phaser的使用是将任务分配到各个子phaser中,并且当子phaser中的任务都已经准备好了,需要通知父phaser。

代码细节分析:

·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;
    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 &&
                (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();
            if ((q == null || q.phase == phase) &&
                (int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
                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;
}

在分析源码前需要思考一下,这是个让线程阻塞的方法,那么肯定会看到park()方法,

但是按照Doug Lea的写法,是绝对不可能直接让线程去阻塞的,所以一定会有优化,这个优化显然是自旋,

为什么需要自旋?也许在当前线程马上要去阻塞的时候,其它线程已经完成任务了,那么就可以不需要自旋。

那么显然自旋是需要在多核CPU下的…经过了这样的思考来看代码:

releaseWaiters(phase-1);  首先就是帮助释放上一阶段阻塞的线程,可以看到代码的设计是多么的美妙
int lastUnarrived = 0; 
static final int SPINS_PER_ARRIVAL = (NCPU < 2) ? 1 : 1 << 8; 可以看到如果NCPU数小于2,那么自旋次数为1,为什么?因为如果只有1个CPU是没必要自旋的,其实这里我认为NCPU < 2,可以修改为NCPU <= 2 ,如果只有2个CPU,应该最好也不要自旋,因为ACPU上线程正在等待另外一个BCPU上的线程完成任务,很明显,BCPU在较快时间内调度到ACPU所需要的那个线程的概率是比较小的,(但是其实也没什么,因为现在谁的CPU还是2核的?所以这里肯定是256次自旋)

while(xxx){ 判断当前phaser的phase和传入的phase是否相同 //意味着阶段数不能被改变
if(node==null) 因为传入的node就是为null,所以第一次会走这里
int unarrived = (int)s & UNARRIVED_MASK; 保存未到达的线程数
if (unarrived != lastUnarrived &&  //如果未到达线程数不为0
   (lastUnarrived = unarrived) < NCPU) //并且未到达线程数(赋给lastUnarrived)小于CPU的核数
   spins += SPINS_PER_ARRIVAL;  //那么让增加自旋的次数
   思考:为什么要 未到达线程数<CPU核数时,需要增加自旋次数呢?
   我认为:比如CPU核数为8,但是我只有5个未到达线程数,试想一下:5个线程分8个CPU,是不是绰绰有余?所以在这里我们可以认	为未到达的线程将会很快被执行到,所以自旋的值得的。
boolean interrupted = Thread.interrupted(); //是否被中断   
if (interrupted || --spins < 0) { // need node to record intr
node = new QNode(this, phase, false, false, 0L); //创建节点
node.wasInterrupted = interrupted; //是否被中断
}
} 
然后此处循环结束:因为是while(),所以只要阶段数没被改变,就会一直执行
再次进来的时候node不为null,头插法入队,入队后再次结束本次循环

再次进入时,node不为null,queued=true,此时再去阻塞(可以看到Doug Lea的代码风格,在AQS中也是,再阻塞前会疯狂挣扎,能不去OS就不去OS阻塞,也许就在这1次或者2次的挣扎中,就不需要去阻塞了,这就是性能的体现!!)

接下来就是出队列然后唤醒,或者被中断唤醒的一系列判断,这里就不赘述了,大家应该都能懂

还可以看到阻塞队列是分为奇数队列和偶数队列的,也就是不同阶段所处的阻塞队列是不同的,互不影响,
也就意味着少了竞争(少了竞争就少了CAS的操作,又是高性能!)

小总结:

  1. register():将参加线程数和未到达线程数都加1
  2. arriveAndAwaitAdvance():将未到达线程数减1
  3. arriveAndDeregister():将参加线程数和未到达线程数都减1
  4. new Phaser(1)的时候往往需要arriveAndDeregister()充当发令抢(一组线程等待某一条件发生后再一起执行)
  5. new Phaser()的时候就是一组线程都到了后再一起执行
  6. 我相信很多人看过如何CAS只能保证一个变量的原子性,看看这个类中的所需要的变量:终止标志位,阶段数位,参加线程数,未到达线程数,4个变量,如何保证原子性?包装成一个变量也即state变量。(下次如果被问到了,直接回答这个类的例子即可)

那么有关Phaser的源码刨析就到这里了,我只讲述了一些重要的方法(没有讲其基本概念,希望能自行补充),还有一些其余的方法并没有讲解,因为该类本来就没什么人用,所以我只针对案例来刨析源码,以上均为个人理解,若有错误请指出(别骂我,多谢!)。

那么有关Phaser的源码刨析就到这里了,我只讲述了一些重要的方法(没有讲其基本概念,希望能自行补充),还有一些其余的方法并没有讲解,因为该类本来就没什么人用,所以我只针对案例来刨析源码,以上均为个人理解,若有错误请指出(别骂我,多谢!)。

  • 42
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值