文章目录
概述
线程是稀缺资源,如果线程被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,所以合理的使用线程池对线程进行统一分配、调优和监控,有巨大的好处。
- 降低资源消耗
- 提高系统响应速度
- 提高线程可管理性
JDK1.5引入了Executor线程池框架,通过把任务的提交和执行进行解耦,我们只需要定义好任务(Runnable),然后将它提交给线程池,而不用关心该任务是如何执行、被哪个线程执行以及什么时候执行。
类结构
Java线程池相关类库全部位于java.util.concurrent包下,类结构如下图:
- Executor : 是最顶层接口,它只包含一个方法execute()
- ExecutorService : 继承自Executor接口,在Executor接口基础上添加了submit()(关于execute与submit区别,会在本文后边详细叙述)方法,并添加了若干线程池状态(关于线程池状态,会在本文后边详细叙述)操作方法,如shutdown(),shutdownNow()等。
- AbstractExecutorService : 继承自ExecutorService,并实现了ExecutorService的若干方法,如submit()等。
- ThreadPoolExecutor : 继承于AbstractExecutorService,是线程池的核心类,几乎所有与线程池有关的逻辑都封装在这个类里边。
- DiscardPolicy、DiscardOldestPolicy、AbortPolicy、CallerRunsPolicy : 四种饱和策略。
- Worker : 线程池真正的线程类,本身实现了Runnable接口和AQS(AbstractQueuedSynchronizer)接口。
详细解析
构造函数
总共有四个,分别为
//1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
//3 ThreadFactory threadFactory)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//4
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造函数主要是对线程池参数的初始化,关于线程池参数可以参考另外一篇文章Java线程池参数解析。其中构造函数1-3最后都是调用到了构造函数4,简单分析一下4即可。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- 首先,对各个数值型参数做校验,校验规则为 : (1) corePoolSize < 0 (2) maximumPoolSize <=0 (3) maximumPoolSize < corePoolSize (4) keepAliveTime < 0,以上情况均抛出IllegalArgumentException异常。
- 然后,对阻塞队列、线程工厂、饱和策略判空,任一项为空则抛出NullPointerException异常。
- 最后,通过校验之后,将各个参数值赋值给ThreadPoolExecutor实例的相应成员变量。
一些成员变量
由于corePoolSize、maximumPoolSize等线程池参数已经在博客Java线程池参数解析有过叙述,故此处不再赘述。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl是线程池中一个非常重要的变量,以它的低29位表示线程池中处于RUNNING状态的线程个数,高3位表示线程池所处的状态(关于线程池的状态我们待会再讲)。
private static final int COUNT_BITS = Integer.SIZE - 3;
COUNT_BITS表示RUNNING状态下的线程数对应二进制的位数,也就是29。
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
CAPACITY用于与ctl做与运算,得到RUNNING状态下线程的个数。CAPACITY的值为
000111111111111111111111111111111
private static final int RUNNING = -1 << COUNT_BITS;
RUNNING表示线程池的运行状态,可处理新任务并执行队列中的任务,十进制表示为-536870912。二进制表示为
11100000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
SHOTDOWN表示线程池的关闭态,不接受新任务,但处理队列中的任务,值为0。
private static final int STOP = 1 << COUNT_BITS;
STOP表示线程池的停止态,不接受新任务,不处理队列中任务,且打断运行中任务,十进制表示为536870912。二进制表示为
00100000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
TIDYING表示线程池的整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法,值为1073741824。二进制表示为
01000000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
TERMINATED表示线程池的结束态,terminated() 方法已完成,值为1610612736。二进制表示为
01100000000000000000000000000000
不难发现,在所有的五种状态中SHUTDOWN值等于0,RUNNING值小于0,其他三种状态STOP、TIDYING、TERMINATED值均大于0。
private final HashSet<Worker> workers = new HashSet<Worker>();
workers用于存储真正运行的任务Worker(后边我们会详细讲述Worker的运行原理)。
private volatile boolean allowCoreThreadTimeOut;
allowCoreThreadTimeOut用于表示核心线程在空闲一定时间之后是否过期。
关于线程池状态
为了更好的管理线程,Java线程池总共包含五种状态。要注意的是,线程池的状态要与线程的状态区别开来,这两者之间没有什么必然的联系。具体请看下图
- RUNNING : 运行态,也是线程池的默认状态,当new一个ThreadPoolExecutor实例之后,这个ThreadPoolExecutor的状态就是运行态。运行态能够接受新添加任务,也能够对阻塞队列中的任务进行处理。
- SHOWDOWN : 关闭态,当调用ThreadPoolExecutor实例的showdown()方法之后,这个ThreadPoolExecutor实例就会进入关闭态。关闭态能够对阻塞队列中的任务进行处理,不能够接受新添加的非空任务,但是可以接受新添加的空任务。
- STOP : 停止态,当调用ThreadPoolExecutor实例的shutdownNow()方法之后,这个ThreadPoolExecutor实例就会进入停止态。停止态不能接受新添加任务,也不能够对阻塞队列中的任务进行处理,并且会中断正在运行的任务。
- TIDYING : 整理态,当线程池中所有任务已被终止, 这个ThreadPoolExecutor实例就会进入停止态。
- TERMINATED : 结束态,当线程池处于整理态,并调用terminated()方法,执行完毕之后,就会进入结束态,此状态也表示整个线程池生命周期的结束。
了解线程池的五个状态,对于理解线程池的工作原理至关重要,它是基础中的基础,所以在读线程池源码前一定要了解一下它们。
添加一个任务之后发生了什么?
接下来,我们主要分析一下execute()方法,也就是添加一个Runnable任务之后,到底发生了什么?
execute()方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWo