ThreadPoolExecutor源码解读(一)

从ThreadPoolExecutor构造函数来看,不得不说的几个参数,核心线程数corePoolSize、最大线程数maximumPoolSize、工作队列workQueue、线程工厂threadFactory以及拒绝策略RejectedExecutionHandler,他们之间有着千丝万缕的关系:

  1. 当创建的线程数小于核心线程数corePoolSize时,提交任务会继续创建新线程执行任务。

  2. 当创建的线程数大于等于corePoolSize时,此时再提交任务将被添加到工作队列workQueue中。

  3. 当工作队列workQueue已满,此时再提交任务会创建新线程,触发第二个阈值的判断maximumPoolSize

  4. 当创建的线程数大于等于最大线程数maximumPoolSize时,此时再提交任务将触发拒绝策略RejectedExecutionHandler

白纸黑字总是苍白的,如下是提交任务至线程池的流程图:

ThreadPoolExecutor源码解读(一)_第1张图片

除了具有主角光环的参数外,还有几个参数决定着工作线程的生死存亡。keepAliveTime决定非核心线程数的线程的存活时长。当线程池中创建的线程数量超过设置的 corePoolSize,在某些线程处理完任务后,如果等待 keepAliveTime时间,仍然没有新的任务分配给它们,那么这些线程就属于空闲线程,将会被回收。线程池回收线程,没有所谓的“核心线程”和“非核心线程”之分,直到线程池的线程数等于最小核心线程数corePoolSize,回收才会停止。

看样子线程池一定会有小于等于corePoolSize数量的线程一直存活,这样如果这个线程池是非核心线程池,一直占用着线程势必会影响到核心线程池的运行,所以核心线程数内的线程也有被回收的需求。

在创建线程池时,构造函数中并没有显式设置核心线程数内的线程过期回收的参数,但是可以通过调用allowCoreThreadTimeOut(true)方法将属性allowCoreThreadTimeOut设置为true,从而使得核心线程数内的线程空闲等待keepAliveTime时间后,依然没有任务分配时被回收。

public void allowCoreThreadTimeOut(boolean value) {

if (value && keepAliveTime <= 0)

throw new IllegalArgumentException(“Core threads must have nonzero keep alive times”);

if (value != allowCoreThreadTimeOut) {

allowCoreThreadTimeOut = value;

if (value)

interruptIdleWorkers();

}

}

[](()三、线程池的生命周期


世间万物都有生死轮回,线程池也不例外,它也有自己的生命周期。而巧妙的是,作者Doug Lea用一个32位的int变量表示两种含义:高3位表示线程池的运行状态,低29位表示工作线程数。

//一个ctl 表示两种含义,高3位为runState,低29为workerCount

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

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

//536870911

//0001 1111 1111 1111 1111 1111 1111 1111

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

// runState is stored in the high-order bits

//-536870912

//111 0 0000 0000 0000 0000 0000 0000 0000

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

//0

//000 0 0000 0000 0000 0000 0000 0000 0000

private static final int SHUTDOWN = 0 << COUNT_BITS;

//536870912

//001 0 0000 0000 0000 0000 0000 0000 0000

private static final int STOP = 1 << COUNT_BITS;

//1073741824

//010 0 0000 0000 0000 0000 0000 0000 0000

private static final int TIDYING = 2 << COUNT_BITS;

//1610612736

//011 0 0000 0000 0000 0000 0000 0000 0000

private static final int TERMINATED = 3 << COUNT_BITS;

// Packing and unpacking ctl

//从变量ctl中解析出runState

//先将CAPACITY做按位非操作,即~n = - ( n+1 ),就是 RUNNING

//然后再做按位与,可得出高3位

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

//从变量ctl中解析出workerCount

//对CAPACITY按位与,可得出低29位

//1 & 1 = 1,1 & 0 = 0,0 & 1 = 0,0 & 0 = 0

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

//将rs和wc转为二进制 再进行按位或计算,位上只要有1就是该位就是1

// 1 & 1 = 1,1 & 0 = 1,0 & 1 = 1,0 & 0 = 0

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

作者是如何将一个32位int变量ctl表示为两个含义的呢?这就涉及到二进制数的位运算:

  • CAPACITY = (1 << COUNT_BITS) - 1,1转为二进制数0000 0000 0000 0000 0000 0000 0000 0001向左移29位,得到001 0 0000 0000 0000 0000 0000 0000 0000,这个过程相当于1*2^29,但是此时得到的是高3位的第一位(最小值),在其基础上减1就是低29位的最大值了,得到0001 1111 1111 1111 1111 1111 1111 1111,将其设置为线程池的容量CAPACITY

  • RUNNING = -1 << COUNT_BITS,创建线程池后,线程池就处于正在运行状态RUNNING,其是-1向左移29位,-1的二进制是1111 1111 1111 1111 1111 1111 1111 1111,向左移29位后剩下3个1,低29位补0,得到1110 0000 0000 0000 0000 0000 0000 0000,正好1占满了高3位。(-1的二进制是1的补码,原码取反+1就是补码,如1的原码是0000 0000 0000 0000 0000 0000 0000 0001,取反后是1111 1111 1111 1111 1111 1111 1111 1110,再加1就是1111 1111 1111 1111 1111 1111 1111 1111。)

  • SHUTDOWN = 0 << COUNT_BITS,当调用shutdown(),线程池进入SHUTDOWN状态,SHUTDOWN是0向左移29位依然是0。

  • STOP = 1 << COUNT_BITS,当调用shutdownNow(),线程池进入STOP状态,1左移29位得到001 0 0000 0000 0000 0000 0000 0000 0000

  • TIDYING = 2 << COUNT_BITSTIDYING是一个过渡状态,当线程池调用shutdown()shutdownNow()后,当线程池中没有正在运行的线程且工作队列为空,此时设置线程池状态为TIDYING。2左移29位得到010 0 0000 0000 0000 0000 0000 0000 0000

  • TERM **《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】** INATED = 3 << COUNT_BITSTERMINATED才是代表线程池真正的寿终正寝。3左移29位得到011 0 0000 0000 0000 0000 0000 0000 0000

还有三个方法才是将两个含义揉捻成一个变量,又分别拆出两个含义:

  • ctlOf(int rs, int wc) { return rs | wc; },运行状态rs和工作线程数wc,二者进行按位或|计算合成一个变量ctl。(|操作,1 & 1 = 1,1 & 0 = 1,0 & 1 = 1,0 & 0 = 0,二者比较,只要位上有1,该位就是1。)

  • runStateOf(int c) { return c & ~CAPACITY; },从中拆出运行状态。先对CAPACITY做按位非~操作,即~CAPACITY = - ( CAPACITY+1 ),就是 RUNNING。然后按位与&操作,可得高3位。(RUNNING高3位都是1,低29位都是0,所以&运算后,1只会出现在高3位,故而可得高3位)

  • workerCountOf(int c) { return c & CAPACITY; },从中拆出工作线程数。对CAPACITY按位与&,可得低29位。(因为CAPACITY低29位都是1,高3位都是0,所以&运算后,1只会出现在低29位,故而可得低29位)

&操作,1 & 1 = 1,1 & 0 = 0,0 & 1 = 0,0 & 0 = 0,二者比较,位上都是1,该位才是1。)

如图所示线程池的生命周期流转图:

ThreadPoolExecutor源码解读(一)_第2张图片

[](()四、ThreadFactory如何创建工作线程


创建线程池时可以不传ThreadFactory,此时会给一个默认线程工厂Executors.defaultThreadFactory(),而它究竟是怎样生产工作线程的呢?

DefaultThreadFactory实现了接口ThreadFactory,其主要做了3件事:

  • 创建工作线程,并设置分组和命名。

  • 工作线程是守护线程时,将线程设置为非守护线程。

  • 设置工作线程默认优先级NORM_PRIORITY

public interface ThreadFactory {

Thread newThread(Runnable r);

}

//java.util.concurrent.Executors.DefaultThreadFactory

static class DefaultThreadFactory implements ThreadFactory {

private static final AtomicInteger poolNumber = new AtomicInteger(1);

private final ThreadGroup group;

private final AtomicInteger threadNumber = new AtomicInteger(1);

private final String namePrefix;

DefaultThreadFactory() {

SecurityManager s = System.getSecurityManager();

group = (s != null) ? s.getThreadGroup() :

Thread.currentThread().getThreadGroup();

namePrefix = “pool-” +

poolNumber.getAndIncrement() +

“-thread-”;

}

public Thread newThread(Runnable r) {

//创建线程时,设置分组和命名

Thread t = new Thread(group, r,

namePrefix + threadNumber.getAndIncrement(),

0);

if (t.isDaemon())

t.setDaemon(false);

if (t.getPriority() != Thread.NORM_PRIORITY)

t.setPriority(Thread.NORM_PRIORITY);

return t;

}

}

用户可以自行实现ThreadFactory接口,设计特殊的线程工厂。

[](()五、四种官方拒接策略


当工作线程数大于等于maximumPoolSize时,此时再提交任务将会触发拒绝策略。

创建线程池时也可不传RejectedExecutionHandler,此时会给一个默认的拒绝策略AbortPolicy

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

AbortPolicy总是会抛出一个RejectedExecutionException异常,再无其他操作。

public static class AbortPolicy implements RejectedExecutionHandler {

public AbortPolicy() { }

/**

  • Always throws RejectedExecutionException.

你可能感兴趣的:(Java,经验分享,架构,java)