ThreadPoolExecutor系列之状态详解

状态定义

线程池ThreadPoolExecutor有两种属性:运行状态和线程数。

线程数很好理解,就是池子中有多少线程;运行状态共有5种,分别是

RUNNING:运行中, 为初始状态,即刚创建的线程池就是此状态。
SHUTDOWN:停工状态,不再接收新任务,但会继续处理队列中的任务。
STOP:停止状态,不再接收新任务,不再处理已有任务,且会中断正在执行的任务。
TIDYING:清理中,所有任务都停止了,且线程数量也降为0。
TERMINATED,终止状态,钩子函数terminated()已经执行完成,线程池彻底销毁。
复制代码

状态流转

五种状态的流转图如下

源码注释写的非常棒,此处贴一下

*   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
复制代码

代码实现

线程数是一个非负整数,可以用一个int来表示。

运行状态固定只有5种,为节省空间,可以用三个bit来表示,因为三个bit最多可以表示8种状态了。

线程池的这两个属性是需要同时访问的,也就是需要保证原子操作,如果按上面分开表示的话,修改时估计还得加一把锁。

那有没有更优雅的实现呢,我们来看一下Doug Lea大牛的实现吧

/**
 * 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
 */
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 表示线程数的bit数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池支持的最大的线程数量(536870911,完全够用了)
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits(运行状态保存在int的高三位)
// 通过注释的数字可以看出,运行状态对应的数字依次增大,这很重要,方便后续的运行状态判断
// 1110 0000 0000 0000 0000 0000 0000 0000(-536870912)
private static final int RUNNING    = -1 << COUNT_BITS;
// 0000 0000 0000 0000 0000 0000 0000 0000(0)
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 0010 0000 0000 0000 0000 0000 0000 0000(536870912)
private static final int STOP       =  1 << COUNT_BITS;
// 0100 0000 0000 0000 0000 0000 0000 0000(1073741824)
private static final int TIDYING    =  2 << COUNT_BITS;
// 0110 0000 0000 0000 0000 0000 0000 0000(1610612736)
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; }
// 组装运行状态和线程数量,成为ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }

/*
 * 因为运行状态存在int的高三位,且依次增大,因此可以巧妙的通过大小比较得知两状态关系
 */
private static boolean runStateLessThan(int c, int s) {
    return c < s;
}

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}
// 判断线程是否在运行
// 此处为啥用 < SHUTDOWN 而不是直接 = RUNNING 呢,因为ctl还有低29位表示的线程数呢
private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}
复制代码

Doug Lea巧妙的使用了位运算,用一个AtomicInteger解决了操作两个属性的原子性问题;同时把状态放在高三位,还可以方便的通过大小比较来的值线程池的状态。

关于为什么使用int而不是long,Doug Lea也很贴心的给出了解释

* 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.
复制代码

用29位表示线程数,已经可以支持5亿个线程了,一般我们会通过-Xss1024K来设置线程栈大小为1M,如果我们达到5亿个线程,光线程栈空间就需要占用至少500T的内存了,真的难以想象我们有一天能到达这个量级。

不过Doug Lea还是很严谨的考虑了这种可能,如果AtomicInteger不够用了,就改用AtomicLong,不过目前来说用AtomicInteger更快更简单,不过度设计,够用就好~

状态操作

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadPoolExecutorJava 提供的用于创建线程池的类,它的构造函数有很多参数,下面是对这些参数的详细解释: 1. corePoolSize:线程池中核心线程的数量。当线程池中的线程数量小于 corePoolSize 时,新的任务会一直创建新的线程直到达到 corePoolSize 个线程。 2. maximumPoolSize:线程池中最大线程数。当线程池中的线程数量达到 corePoolSize 后,新的任务会被放入到等待队列中,等待被执行。如果等待队列已满,且线程池中的线程数量小于 maximumPoolSize,则会创建新的线程执行任务。 3. keepAliveTime:线程池中非核心线程的超时时间。当线程池中的线程数量大于 corePoolSize 时,多余的线程会被回收,但回收前会等待 keepAliveTime 时间,如果在这个时间内没有新的任务需要执行,则这个线程会被终止。 4. TimeUnit:超时时间的单位。 5. workQueue:用于缓存等待执行的任务的队列。ThreadPoolExecutor 提供了多种队列,如 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue 等。可以根据需求选择不同的队列。 6. threadFactory:用于创建新的线程。ThreadPoolExecutor 默认使用 Executors.defaultThreadFactory() 创建线程。如果需要自定义线程创建方式,可以实现 ThreadFactory 接口。 7. handler:线程池中的线程数量达到 maximumPoolSize,并且等待队列已满时,新的任务的处理策略。ThreadPoolExecutor 提供了 4 种策略: - AbortPolicy:直接抛出异常; - CallerRunsPolicy:不在新线程中执行任务,而是让调用 execute 方法的线程执行任务; - DiscardOldestPolicy:丢弃最老的任务,执行当前任务; - DiscardPolicy:直接丢弃任务。 这些参数可以根据实际需求进行调整,以达到最优的线程池效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值