线程池内属性和状态转换详解

背景

在学习,线程池时,经常因为其内部属性感到疑惑,这篇文章就是逐个解析各个属性的用途。

属性转二进制

首先介绍一个开始就稀里糊涂的几个参数值。看着是真劝退。

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

    /**
     * 1:  1 << 29 为 1左移29  结果: 10 0000 0000 0000 0000 0000 0000 0000
     * 2: 减一。 CAPACITY =  11111 11111111 11111111 11111111     536870911
     */
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    /**
     * runState is stored in the high-order bits
     * -1 左移 29位
     * -1 的二进制:
     *      原码:10000000 00000000 00000000 00000001
     *      补码:11111111 11111111 11111111 11111111
     * 补码左移。11100000  00000000 00000000 00000000   -536870912
     */
    private static final int RUNNING    = -1 << COUNT_BITS;
    // 00000000 00000000 00000000 00000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //00100000 00000000 00000000 00000000          536870912
    private static final int STOP       =  1 << COUNT_BITS;
    //01000000 00000000 00000000 00000000          1073741824
    private static final int TIDYING    =  2 << COUNT_BITS;
    //01100000 00000000 00000000 00000000          1073741824+536870912
    private static final int TERMINATED =  3 << COUNT_BITS;

至于这几个值分别有什么意义,后面会给出详细介绍。

一、ThreadPoolExecutor 中的重要成员变量

1、AtomicInteger ctl

AtomicInteger 类型的 ctl 代表了 ThreadPoolExecutor 中的控制状态,它是一个复核类型的成员变量,是一个原子整数,借助高低位包装了两个概念:

(1)workerCount:线程池中当前活动的线程数量,占据 ctl 的低 29 位;就是COUNT_BITS

(2)unState:线程池运行状态,占据 ctl 的高 3 位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五种状态。

AtomicInteger ctl的定义如下:

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

先说下workerCount:线程池中当前活动的线程数量,它占据 ctl 的低 29 位,这样,每当活跃线程数增加或减少时,ctl 直接做相应数目的增减即可,十分方便。而 ThreadPoolExecutor 中 COUNT_BITS 就代表了 workerCount 所占位数,定义如下:

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

在 Java 中,一个 int 占据 32 位,而 COUNT_BITS 的结果不言而喻,Integer 大小 32 减去 3,就是 29;另外,既然 workerCount 代表了线程池中当前活动的线程数量,那么它肯定有个上下限阈值,下限很明显就是 0,上限呢?ThreadPoolExecutor 中 CAPACITY 就代表了 workerCount 的上限,它是 ThreadPoolExecutor 中理论上的最大活跃线程数,其定义如下:

    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

运算过程为 1 左移 29 位,也就是 00000000 00000000 00000000 00000001 --> 001 0000 00000000 00000000 00000000,再减去 1 的话,就是 000 11111 11111111 11111111 11111111,前三位代表线程池运行状态 runState,所以这里 workerCount 的理论最大值就应该是 29 个 1,即 536870911;

既然 workerCount作为其中一个概念复合在 AtomicInteger ctl 中,那么 ThreadPoolExecutor 理应提供从 AtomicInteger ctl 中解析出 workerCount 的方法,如下:

private static int workerCountOf(int c)  { return c & CAPACITY; }

计算逻辑很简单,传入的 c 代表的是 ctl 的值,即高 3 位为线程池运行状态 runState,低 29 位为线程池中当前活动的线程数量 workerCount,将其与 CAPACITY 进行与操作 &,也就是与 000 11111 11111111 11111111 11111111 进行与操作.
c 的前三位通过与 000 进行与操作,无论 c 前三位为何值,最终都会变成 000,也就是舍弃前三位的值.
而 c 的低 29 位与 29 个 1 进行与操作,c 的低 29 位还是会保持原值,这样就从 AtomicInteger ctl 中解析出了 workerCount 的值。

接下来,我们再看下 runState:线程池运行状态,它占据 ctl 的高 3 位,有 RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五种状态。我们先分别解释下这五种状态:

(1)RUNNING(前三位:111):接受新任务,并处理队列任务

private static final int RUNNING    = -1 << COUNT_BITS;

-1 在 Java 底层是由 32 个 1 表示的,左移 29 位的话,即 111 00000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位全部为 1 的话,表示 RUNNING 状态,即 - 536870912;

(2)SHUTDOWN(前三位:000):不接受新任务,但会处理队列任务

    private static final int SHUTDOWN   =  0 << COUNT_BITS;

0 在 Java 底层是由 32 个 0 表示的,无论左移多少位,还是 32 个 0,即 000 00000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位全部为 0 的话,表示 SHUTDOWN 状态,即 0;

(3)STOP(前三位:001):不接受新任务,不会处理队列任务,而且会中断正在处理过程中的任务 。

    private static final int STOP       =  1 << COUNT_BITS;

1 在 Java 底层是由前面的 31 个 0 和 1 个 1 组成的,左移 29 位的话,即 001 00000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位为 001 的话,表示 STOP 状态,即 536870912;

(4)TIDYING(前三位:010):所有的任务已结束,workerCount 为 0,线程过渡到 TIDYING 状态,将会执行 terminated() 钩子方法

    private static final int TIDYING    =  2 << COUNT_BITS;

2 在 Java 底层是由前面的 30 个 0 和 1 个 10 组成的,左移 29 位的话,即 010 00000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位为 010 的话,表示 TIDYING 状态,即 1073741824;

(5)TERMINATED(前三位:011):terminated() 方法已经完成

    private static final int TERMINATED =  3 << COUNT_BITS;

3在 Java 底层是由前面的 30 个 0 和 1 个 11 组成的,左移 29 位的话,即 011 00000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位为 011 的话,表示 TERMINATED 状态,即 1610612736;

由上面我们可以得知,运行状态的值按照RUNNING-->SHUTDOWN-->STOP-->TIDYING-->TERMINATED顺序值是递增的,这些值之间的数值顺序很重要。随着时间的推移,运行状态单调增加,但是不需要经过每个状态。那么,可能存在的线程池状态的转换是什么呢?如下:
在这里插入图片描述

(1)RUNNING -> SHUTDOWN:调用 shutdown 方法后,或者线程池实现了 finalize 方法,在里面调用了 shutdown 方法,即隐式调用;

(2)(RUNNING or SHUTDOWN) -> STOP:调用 shutdownNow() 方法后;

(3)SHUTDOWN -> TIDYING:线程池和队列均为空时;

(4)STOP -> TIDYING:线程池为空时;

(5)TIDYING -> TERMINATED:terminated() 钩子方法完成时。

我们再来看下是实现获取运行状态的 runStateOf() 方法,代码如下:

private static int runStateOf(int c)     { return c & ~CAPACITY; }

~ 是按位取反的意思,CAPACITY 表示的是高位的 3 个 0,和低位的 29 个 1,
而~ CAPACITY 则表示高位的 3 个 1,2 低位的 9 个 0,然后再与入参 c 执行按位与操作,
高 3 位保持原样,低 29 位全部设置为 0,也就获取了线程池的运行状态 runState。

最后,我们再看下原子变量 ctl 的初始化方法 ctlOf(),代码如下:

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

很简单,传入的 rs 表示线程池运行状态 runState,其是高 3 位有值,低 29 位全部为 0 的 int,而 wc 则代表线程池中有效线程的数量 workerCount,其为高 3 位全部为 0,而低 29 位有值得 int,将 runState 和 workerCount 做或操作 | 处理,即用 runState 的高 3 位,workerCount 的低 29 位填充的数字,而默认传入的 runState、workerCount 分别为 RUNNING 和 0。

2、BlockingQueue workQueue

workQueue 是用于持有任务并将其转换成工作线程 worker 的队列;

3、HashSet workers

workers 是包含线程池中所有工作线程 worker 的集合,仅仅当拥有 mainLock 锁时才能访问它;

4、long completedTaskCount

completedTaskCount 是已完成任务的计数器,只有在 worker 线程的终止,仅仅当拥有 mainLock 锁时才能访问它;

5、volatile ThreadFactory threadFactory

创建新线程的工厂类;

6、volatile RejectedExecutionHandler handler

执行过程中 shutdown 时调用的 handler;

7、volatile long keepAliveTime

空闲线程等待工作的超时时间(纳秒),即空闲线程存活时间;

8、volatile boolean allowCoreThreadTimeOut

默认值为 false,如果为 false,core 线程在空闲时依然存活;如果为 true,则 core 线程等待工作,直到时间超时至 keepAliveTime;

9、volatile int corePoolSize

核心线程池大小,保持存活的工作线程的最小数目,当小于 corePoolSize 时,会直接启动新的一个线程来处理任务,而不管线程池中是否有空闲线程;

10、volatile int maximumPoolSize

线程池最大大小,也就是线程池中线程的最大数量。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值