线程池源码分析

java 中,线程池 ThreadPoolExecutor 是一个绕不过去的类,它是享元模式思想的体现,通过在容器中创建一定数量的线程加以重复利用,从而避免频繁创建线程带来的额外开销。一个设置合理的线程池可以提高任务响应的速度,并且避免线程数超过硬件能力带来的意外情况。

在本文,将深入线程池源码,了解线程池的底层实现与运行机制。

一、构造方法

ThreadPoolExecutor 类一共提供了四个构造方法,我们基于参数最完整构造方法了解一下线程池创建所需要的变量:

public ThreadPoolExecutor(int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 非核心线程闲置存活时间 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 工作队列 ThreadFactory threadFactory, // 创建线程使用的线程工厂 RejectedExecutionHandler handler // 拒绝策略) { }

核心线程数:即长期存在的线程数,当线程池中运行线程未达到核心线程数时会优先创建新线程;

最大线程数:当核心线程已满,工作队列已满,同时线程池中线程总数未超过最大线程数,会创建非核心线程;

非核心线程闲置存活时间:当非核心线程闲置的时的最大存活时间;

时间单位:非核心线程闲置存活时间的时间单位;

任务队列:当核心线程满后,任务会优先加入工作队列,等等待核心线程消费;

线程工厂:线程池创建新线程时使用的线程工厂;

拒绝策略:当工作队列与线程池都满时,用于执行的策略;

二、线程池状态 1.线程池状态

线程池拥有一个 AtomicInteger 类型的成员变量 ctl ,通过位运算分别使用 ctl 的高位低位以便在一个值中存储线程数量以及线程池状态。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 29(32-3) private static final int COUNT_BITS = Integer.SIZE - 3; // 允许的最大工作线程(2^29-1 约5亿) private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 运行状态。线程池接受并处理新任务 private static final int RUNNING = -1 << COUNT_BITS; // 关闭状态。线程池不能接受新任务,处理完剩余任务后关闭。调用shutdown()方法会进入该状态。 private static final int SHUTDOWN = 0 << COUNT_BITS; // 停止状态。线程池不能接受新任务,并且尝试中断旧任务。调用shutdownNow()方法会进入该状态。 private static final int STOP = 1 << COUNT_BITS; // 整理状态。由关闭状态转变,线程池任务队列为空时进入该状态,会调用terminated()方法。 private static final int TIDYING = 2 << COUNT_BITS; // 终止状态。terminated()方法执行完毕后进入该状态,线程池彻底停止。 private static final int TERMINATED = 3 << COUNT_BITS; 2.线程状态的计算

这里比较不好理解的是上述-1的位运算,下面我们来分析一下:

在计算机中,二进制负数一般用补码表示,即源码取反再加一。但又有这种说法,即将最高位作为符号位,0为正数,1为负数。实际上两者是可以结合在一起看的。假如数字是单字节数,1 字节对应8 bit,即八位,现在,我们要计算 - 1。

按照第二种说法,最高位为符号位,则有 1/000 0001,然后按第一种说法取反后+1,并且符号位不变,则有 1/111 1110 + 1,即 1/111 1111。

现在回到 -1 << COUNT_BITS这行代码:

一个 int 是 4 个字节,对应 32 bit,按上述过程 -1 转为二进制即为 1/111......1111(32个1), COUNT_BITS是 29,-1 左移 29 位,最终得到 111.0...0000。

同理,计算其他的几种状态,可知分别是:

状态 二进制
RUNNING   111...0....00  
SHUTDOWN   000...0....00  
STOP   001...0....00  
TIDYING   010...0....00  
TERMINATED   011...0....00  

其中,我们可以知道 SHUTDOWN 状态转为十进制也是 0 ,而 RUNNING 作为有符号数,它的最高位是 1,说明转为十进制以后是个负数,其他的状态最高位都是 0,转为十进制之后都是正数,也就是说,我们可以这么认为:

小于 SHUTDOWN 的就是 RUNNING,大于 SHUTDOWN 就是停止或者停止中。

这也是后面状态计算的一些写法的基础。比如 isRunning()方法:

private static boolean isRunning(int c) { return c < SHUTDOWN; } 3.线程状态与工作线程数的获取 // 根据当前运行状态和工作线程数获取当前的 ctl private static int ctlOf(int rs, int wc) { return rs | wc; } // 获取运行状态 private static int runStateOf(int c) { return c & ~CAPACITY; } // 获取工作线程数 private static int workerCountOf(int c) { return c & CAPACITY; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdwgg.html