再探线程池 ThreadPoolExecutor - 状态字段解析

182 阅读7分钟

状态 ctl

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

来看它的注释:

The main pool control state, ctl, is an atomic integer packing two conceptual fields workerCount, indicating the effective number of threads runState, indicating whether running, shutting down etc In order to pack them into one int, we limit workerCount to (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2 billion) otherwise representable. If this is ever an issue in the future, the variable can be changed to be an AtomicLong, and the shift/mask constants below adjusted. But until the need arises, this code is a bit faster and simpler using an int. The workerCount is the number of workers that have been permitted to start and not permitted to stop. The value may be transiently different from the actual number of live threads, for example when a ThreadFactory fails to create a thread when asked, and when exiting threads are still performing bookkeeping before terminating. The user-visible pool size is reported as the current size of the workers set. The runState provides the main lifecycle control, taking on values: RUNNING: Accept new tasks and process queued tasks SHUTDOWN: Don't accept new tasks, but process queued tasks STOP: Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks TIDYING: All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method TERMINATED: terminated() has completed The numerical order among these values matters, to allow ordered comparisons. The runState monotonically increases over time, but need not hit each state. The transitions are: RUNNING -> SHUTDOWN On invocation of shutdown(), perhaps implicitly in finalize() (RUNNING or SHUTDOWN) -> STOP On invocation of shutdownNow() SHUTDOWN -> TIDYING When both queue and pool are empty STOP -> TIDYING When pool is empty TIDYING -> TERMINATED When the terminated() hook method has completed Threads waiting in awaitTermination() will return when the state reaches TERMINATED. Detecting the transition from SHUTDOWN to TIDYING is less straightforward than you'd like because the queue may become empty after non-empty and vice versa during SHUTDOWN state, but we can only terminate if, after seeing that it is empty, we see that workerCount is 0 (which sometimes entails a recheck -- see below).

这是线程池状态控制,是一个原子integer, 包装两个字段, 工作线程数和是否运行中、关闭等状态。 为了把他们打包成一个int, 我们限制 工作线程数 为 (2^29)-1 大约5亿个线程而不是 2^31 -1 (20亿) 。 如果这在将来成为一个问题,变量可以被改成AtomicLong, 以及调整下面的移位/掩模常数。

WorkCount是已经被允许启动但不被允许停止的工作线程数。该值可能暂时不同于实际活跃线程数,例如 当一个线程工厂创建线程失败,以及退出线程在终止前仍在执行时。 用户可见的池大小是当前工作数的集合。 runState 提供主要的生命周期控制, 取值:

  • RUNNING: 接受新任务处理队列的任务
  • SHUTDOWN: 不接受新任务,处理队列中的任务
  • STOP:不接受新任务,不处理队列任务,中断正在执行的任务
  • TIDYING:所有的线程已经终止,WorkerCount是0, 线程过渡到状态TIDYING 将会运行 terminated() 钩子方法
  • TERMINATED: terminated()方法运行完成。

为了进行有序的比较,这些值之间的数值顺序很重要。RunState 随着时间单调地增加,但不需要到达每个状态。 过渡阶段包括:

  • RUNNING -> SHUTDOWN 在调用 shutdown()时候,可能在finalize()时候隐式调用
  • (RUNNING or SHUTDOWN) -> STOP 调用shutdownNow()时候
  • SHUTDOWN -> TIDYING 当队列和池子都为空时候
  • STOP -> TIDYING 当池子是空的时候
  • TIDYING -> TERMINATED 当terminated()钩子方法完成

等待在awaitTermination()的线程将在状态到达TERMINATED时候返回(return).

检测从SHUTDOWN到TIDYING的转换并不像你希望的那么简单,因为队列可能在非空之后变成空, 反之在SHUTDOWN阶段也是, 但是我们只能看到它是空的时候, WorkCount是0 的时候,才能关闭它,(这有时需要重新检查)。

状态的具体值

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));  // 0
private static final int COUNT_BITS = Integer.SIZE - 3;  // 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;  // 536870911

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;   // -536870912
private static final int SHUTDOWN   =  0 << COUNT_BITS;   // 0
private static final int STOP       =  1 << COUNT_BITS;   // 536870912
private static final int TIDYING    =  2 << COUNT_BITS;   // 1073741824
private static final int TERMINATED =  3 << COUNT_BITS;   // 1610612736

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }  // ~CAPACITY == -536870912
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

状态范围显示在图上就是如下:

image.png

但是,也可以从二进制角度更直观:

状态起始值二进制
RUNNING-1 << 291110 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN0 << 290000 0000 0000 0000 0000 0000 0000 0000
STOP1 << 290010 0000 0000 0000 0000 0000 0000 0000
TIDYING2 << 290100 0000 0000 0000 0000 0000 0000 0000
TERMINATED3 << 290110 0000 0000 0000 0000 0000 0000 0000

简单理解为, int 数值共32位, 前面3位是固定的值来区分状态, 后面29位用来记录workerCount

分析状态的打包和解包

根据注释,我们知道线程池状态是范围数字表示,最大范围是 CAPACITY = (1<< 29)-1

打包和解包代码:

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

打包用ctlOf, 解包runStateOf, workerCountOf. 所以,这里有个疑问,

为何 ctl = runState | workerCount , 还可以用 ctl & CAPACITY 解包出来?

要直接想的话还是有点困难,但是看着二进制数据再思考就直观多了:

状态起始值二进制
CAPACITY1<<29 - 10001 1111 1111 1111 1111 1111 1111 1111
~CAPACITY-1 << 291110 0000 0000 0000 0000 0000 0000 0000
RUNNING-1 << 291110 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN0 << 290000 0000 0000 0000 0000 0000 0000 0000
STOP1 << 290010 0000 0000 0000 0000 0000 0000 0000
TIDYING2 << 290100 0000 0000 0000 0000 0000 0000 0000
TERMINATED3 << 290110 0000 0000 0000 0000 0000 0000 0000

这里我增加了 CAPACITY 和 ~CAPACITY的值来辅助观察。

看打包代码:

private static int ctlOf(int rs, int wc) {
    // 这里用 状态 和 workerCount 做了按位或
    // 在二进制上体现,其实就是把 前三位的状态位数据  和 后29位的wokerCount数据做了合并
    // 由于使用的位不同,所以是互不影响的
    return rs | wc;
}

再来看解包代码:


// 获取状态
private static int runStateOf(int c)     { 
    // 拿 ctl 和 ~CAPACITY 做按位与
    // 其实就是只取状态位上的数, 也就是要构造 111 0 和 具体的位数字来按位与
    // 111 和 x 按位与的结果就是 x,  同时也把低位(其他29位)改成了0
    return c & ~CAPACITY; 
    
    
}

// 获取工作线程数
private static int workerCountOf(int c)  { 
    // 拿 ctl 和 CAPACITY 做按位与
    // 其实就是状态位全是0, 低位29位全是1 
    // 0001 ...1   &   x =  0001 ...x 
    return c & CAPACITY; 
}

总结

把数据转换为二进制数据后来简化看, 32位int, 高3位是固定值的状态位, 低29位是记录线程数,通过巧妙的位操作来存储两方面的值,很是精巧。

状态更新

ctl是如何更新的?

/**
 * Attempts to CAS-increment the workerCount field of ctl.
 */
private boolean compareAndIncrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect + 1);
}

/**
 * Attempts to CAS-decrement the workerCount field of ctl.
 */
private boolean compareAndDecrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect - 1);
}

主要是使用CAS更新, 数据加1 或 减1