线程池(二、ctl 的设计分析)

一、基础概述

通过源码我们知道线程池ThreadPoolExecutor内部是通过AtomicInteger类型的 ctl变量来控制运行状态和线程个数;在通过AtomicInteger源码知道器内部就是维护了一个int类型的value值,如下

private volatile int value;

那么通过一个值如何维护状态和个数两个值呢?那么接下来通过底层源码来看大神Doug Lea是如何设计的。

二、源码分析

public class ThreadPoolExecutor extends AbstractExecutorService {
    /**
     * 【该段注释已经说明了设计思想,可以好好的看下】
     * 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).
     */

    // 初始化ctl值为0,并设置状态为RUNNING
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // Integer.SIZE = 32,即取29位作为计数
    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; }
    // 计算clt个数
    private static int ctlOf(int rs, int wc) { return rs | wc; }


}

通过以上我们知道线程池维护了五种状态,分别为:

RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED

且根据 AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)) 可以判断初始状态为RUNNING状态。

一、clt状态和数值布局分析

   

    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;

/**
 * 各个值的二进制表示:
 *
 * 1111 1111 1111 1111 1111 1111 1111 1111 (-1) 
 * 0000 0000 0000 0000 0000 0000 0000 0000 (0) 
 * 0000 0000 0000 0000 0000 0000 0000 0001 (1) 
 * 0000 0000 0000 0000 0000 0000 0000 0010 (2) 
 * 0000 0000 0000 0000 0000 0000 0000 0011 (3)
 *
 * 【分析】:
 * 初始容量值,高三位全是0,低29位全是1;后续操作会以此为基础进行操作
 * CAPACITY                    000 1   1111 1111 1111 1111 1111 1111 1111
 *
 *              ---------------3位-1位 -28位---
 * 【前三位,表明状态位,后29位表明线程个数,即 2^29 - 1 基本够用了】
 *
 * RUNNING(-536870912)         111 0    0000 0000 0000 0000 0000 0000 0000
 * SHUTDOWN(0)                 000 0    0000 0000 0000 0000 0000 0000 0000
 * STOP(536870912)             001 0    0000 0000 0000 0000 0000 0000 0000
 * TIDYING(1073741824)         010 0    0000 0000 0000 0000 0000 0000 0000
 * TERMINATED(1610612736)      011 0    0000 0000 0000 0000 0000 0000 0000
 * 
 */
状态

源码表示

 

A << X 表示

A二进制表示向左移动X位

COUNT_BITS = 29

 

线程池

状态位

工作线程个数

前3位

后29位
CAPACITY(1 << COUNT_BITS) - 10001 1111 1111 1111  1111   1111 1111  1111
RUNNING-1 << COUNT_BITS11100000 0000 0000 0000 0000 0000 0000
SHUTDOWN0 << COUNT_BITS00000000 0000 0000 0000 0000 0000 0000
STOP1 << COUNT_BITS00100000 0000 0000 0000 0000 0000 0000
TIDYING2 << COUNT_BITS01000000 0000 0000 0000 0000 0000 0000
TERMINATED3 << COUNT_BITS01100000 0000 0000 0000 0000 0000 0000

根据以上分析得:

    RUNNING            -- 对应的高3位值是111。
    SHUTDOWN           -- 对应的高3位值是000。
    STOP               -- 对应的高3位值是001。
    TIDYING            -- 对应的高3位值是010。
    TERMINATED         -- 对应的高3位值是011。

 

二、基本方法分析

 

1) 状态方法

    // 判断当前运行状态
    private static int runStateOf(int c) { 
        return c & ~CAPACITY; 
    }

 

根据方法,我们知道CAPACITY取反后与c进行&操作,得到状态位

分析:                    

CAPACITY    000 1  1111 1111 1111 1111 1111 1111 1111

先取反操作得到

CAPACITY    111 0  0000 0000 0000 0000 0000 0000 0000

我们可以看到,此时高三位已经变成了1,低29位全部是0,那么我们与c进行&操作则可以得到高三位的值,从而获取到线程池的状态位。

核心:将CAPACITY低29位取0,高3位取1,然后和c进行&操作即可,保证和设计上一致。

2) 工作线程个数方法

    // 工作线程数
    private static int workerCountOf(int c) { 
        return c & CAPACITY; 
    }

分析:

假如c=0,即初始化状态,ctl值为默认值0;

那么我们如何计算才能计算出工作线程数呢(此时工作线程数为0)?

我们知道低29位用来存工作线程个数,那么此时的CAPACITY值如下,高3位0,低29位全是1,那么要计算当前clt的工作线程个数,则只需要将clt的值 和 CAPACITY 进行&操作即可。           

CAPACITY                    000 1   1111  1111  1111  1111  1111  1111  1111

&

c                                    000 0    0000 0000 0000 0000 0000 0000 0000

得到结果为0,即当前工作线程个数为0。

核心:将CAPACITY高3位取0,低29位取1,然后和c进行&操作即可,保证和设计上一致。

 

3) clt值计算方法

// rs 运行状态
// wc 工作线程个数
private static int ctlOf(int rs, int wc) { 
    // 两者进行或(|)操作,还原高3位和低29位的所有值,结果即ctl值
    return rs | wc; 
}
// 如下方法应用,先通过ctlOf(TIDYING, 0)方法计算ctl值,然后在通过compareAndSet设置最新的ctl值。
ctl.compareAndSet(c, ctlOf(TIDYING, 0))

核心:将CAPACITY高3位,低29位,进行或(|)操作即可还原ctl,保证和设计上一致。

 

不得不说,大佬设计的东西太特么完美了.

 

三、各种线程池状态的任务处理说明

 

RUNNING:处于RUNNING状态的线程池能够接受新任务,以及对新添加的任务进行处理。

SHUTDOWN:处于SHUTDOWN状态的线程池不可以接受新任务,但是可以对已添加的任务进行处理。

STOP:处于STOP状态的线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

TIDYING:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。

TERMINATED:线程池彻底终止的状态。

总结:

以上回答了 线程池(一、基础理论)中的2个问题

  1. 线程池中运行状态和工作线程个数的设计原理
  2. 各种运行状态对任务的处理

参考链接:

http://topjava.cn/article/1391298033492299776

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值