JUC并发包

ThreadPoolExecutor源码历险-位运算技巧

2021-01-12  本文已影响0人  于情于你

我体会到的ThreadPoolExcutor的源码阅读可分为几部分

1.ThreadPoolExcutor构造函数(上一篇简书里面已经写过了)
2.位运算技巧
3.不可变内部类Worker
4.主流程常用方法
5.拒绝策略静态内部类
6.工作队列类型

构造函数就不多说了,请参考jdk源码或者移步上一篇

重要属性及位运算技巧

要说ThreadPoolExcutor里面的位运算技巧,首先要了解核心原子属性ctl

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl是主要的池状态控制变量,是一个包装了两个概念字段的原子整数。
     1.workerCount 有效线程数
     2.runState 运行状态,运行、关闭等。
ctlOf的实现:

    private static int ctlOf(int rs, int wc) { return rs | wc; }

也就是拿运行状态(rs),和有效线程数(wc)做位运算,位运算的意义是把这两个属性给整合到一块,比如:

STOP       001,00000000000000000000000000000
WorkCount:000,00000000000000000000000000001
ctl的最终值:001,00000000000000000000000000001

    这里注意在ThreadPoolExcutor里面,状态有5种,并且只占最高位3位(左移了29位),剩下的低29位是给工作线程数量(wc)留的。
    状态占用3位的原因是状态有5种,表示5种状态最少要用3位。

    private static final int COUNT_BITS = Integer.SIZE - 3;

    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;

5种状态的解释:

RUNNING:接受新任务,并且可以处理任务队列中的任务
SHUTDOWN:不接受新任务,但是会处理队列中的剩余任务
STOP:不接受新任务,不处理排队任务以及中断进行中的任务
TIDYING:所有任务都已终止,workerCount为零,转换为状态TIDYING的线程将运行终止的hook方法terminated()

TERMINATED:terminated()方法执行完毕

这里有两个重要的方法runStateOf、workerCountOf要说一下,上面说过ThreadPoolExcutor里面状态会占用ctl的高三位,所以剩下的29位是用来表示线程数量的。

    // 线程数最大限制
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 获取线程池运行状态
   // ~CAPACITY的高三位是1,低29位是0。所以 c & ~CAPACITY 保留了前三位(状态)
    private static int runStateOf(int c)     { return c & ~CAPACITY; }

    // 获取线程有效线程数
   // CAPACITY的高三位是0,低29位是1。所以 c & CAPACITY 保留了低29位(线程数)
    private static int workerCountOf(int c)  { return c & CAPACITY; }

    由于ThreadPool源码篇幅比较长,所以我打算放到几篇里面来写。要不然一篇时间比较长,读起来大家也会失去耐心。其实ThreadPoolExecutor源码读起来比较容易懂,只要认真去看。大家熟悉了位运算技巧,后面代码逻辑都不是事。
    后期我也会把我读源码中遇到的迷惑的问题抛出来,大家可以一起看一下。有知道的可以评论回答我

上一篇下一篇

猜你喜欢

热点阅读