并发控制工具相位器Phaser实现原理

Phaser示意图为方便理解Phaser的内部状态我们看下面的示意图 , 刚开始通过 new Phaser(3) 创建一个相位器对象 , 该对象有三个参与者 。 假如又调用了register()方法则相位器一共有4个参与者 , 这些参与者都处于未到达状态 。 然后某个线程先执行arriveAndAwaitAdvance方法后变为已到达状态 , 接着第二第三个参与者都变为已到达状态 。 假如第四个线程执行了arriveAndDeregister方法则第四个参与者到达后将进行反注册 , 此时只剩三个参与者 。 接着将进入下一阶段 , 相位器会自动调用onAdvance方法判断是否终止相位器 , 如果还未终止则进入下一阶段 , 此时三个参与者又处于未到达状态 。
并发控制工具相位器Phaser实现原理文章插图
示意图
共享状态的表示相位器的底层实现需要对共享变量进行维护 , 并且需要使用硬件基本的CAS操作 , 相位器需要维护的共享变量包括终止状态、当前阶段数、参与者数量以及未到达数量等四个属性 。 这样就要求我们要保证相位器内部属性的一致性 , 我们当然可以定义四个变量来分别表示相位器的四个属性 , 但为了方便维护共享变量 , 可以使用一个long类型作为共享变量 , 这样就能很方便进行硬件基本的原子更新 。 所以现在的重点工作就是对64位的long类型进行划分 , 0-15位表示未到达参与者数量、16-31位表示参与者数量、32-62位表示当前阶段数、63位表示终止状态 。
并发控制工具相位器Phaser实现原理文章插图
状态表示
实现原理通过前面的相位器相关概念的介绍以及三个例子 , 我们已经学会了相位器的使用 , 接下去我们往深一层看相位器是如何实现的 。 以下分析的代码出自JDK源码但却并非完全相同 , 作者去掉了大量的非核心(并发性能优化)代码 , 只保留了最核心的代码 , 这样能保证代码简洁 , 以便能使更好地理解实现原理 。
我们先看Phaser类包含的一些属性以及构造方法 。 state变量即是我们前面讨论到的共享状态 , 它是一个long型 , 我们会使用不同的位范围来表示相位器的内部属性 。 而且通过VarHandle对象来管理共享状态state , 通过它能方便地进行CAS操作 。 MAX_PHASE=Integer.MAX_VALUE表示允许的最大阶段数 , PARTIES_SHIFT=16表示参与者数量偏移 , PHASE_SHIFT=32表示当前阶段偏移 , UNARRIVED_MASK=0xffff表示未到达掩码 , PARTIES_MASK=0xffff0000L表示参与者掩码 , TERMINATION_BIT=1L<<63 表示终止位 , EMPTY=1表示相位器是空的 。 提供两种构造函数 , 可传入参与者数量 , 不带参数时默认参与数为0 , 初始时当前阶段为0 , 然后通过偏移将当前阶段和参与者数量存放到共享变量state中 。
public class Phaser { private static final VarHandle STATE; static {try {MethodHandles.Lookup l = MethodHandles.lookup();STATE = l.findVarHandle(Phaser.class, "state", long.class);} catch (ReflectiveOperationException e) {throw new ExceptionInInitializerError(e);} } private volatile long state; 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; private static final long PARTIES_MASK = 0xffff0000L; private static final long TERMINATION_BIT = 1L << 63; private static final int EMPTY = 1; public Phaser() {this(0); } public Phaser(int parties) {int phase = 0;this.state = (parties == 0) ? (long) EMPTY: ((long) phase << PHASE_SHIFT) | ((long) parties << PARTIES_SHIFT)| ((long) parties); }...}