前言
本文主要内容为分析 ThreadPoolExecutor
线程池的执行原理,然后前后会穿插一些线程池的其他相关内容(构造方法,线程池状态 等等),本文最终目的是为深入分析 ThreadPoolExecutor
源码打下基础。
阅读前请对线程池的概念及作用有所了解(本文不会涉及)。
另外,本文主要内容基于 jdk1.8 来说明。
正文
大纲
- 线程池的构造方法
- 线程池的创建方式
- 线程池的工作状态
- 线程池的工作原理
- 线程池
Worker
内部类
ThreadPoolExecutor
的构造方法
ThreadPoolExecutor
总共有四个构造方法,这里只贴出一个,因为其他三个构造最终还是会调用到此方法
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
// 参数初始化
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造参数详解
先对每个字段的含义有个大概印象,后面 线程池原理 部分会把这些参数串起来讲。
-
int corePoolSize
线程池核心线程数量
-
int maximumPoolSize
线程池最大线程数量
-
long keepAliveTime
线程池中大于 corePoolSize 的那部分线程(处于闲置状态)的最大存活时间
-
TimeUnit unit
存活时间单位
-
BlockingQueue workQueue
保存等待执行的任务的一个阻塞队列
-
ThreadFactory threadFactory
创建线程的一个工厂, 默认为DefaultThreadFactory类
-
RejectedExecutionHandler handler
线程饱和策略,默认为ThreadPoolExecutor.AbortPolicy。
创建线程池的方式
-
通过
ThreadPoolExecutor
的构造函数创建 -
Executors.newCachedThreadPool()
创建一个可缓存的无界线程池,该方法无参数。当线程池中的线程空闲时间超过60s则会自动回收该线程,当任务超过线程池的线程数则创建新线程。线程池的大小上限为Integer.MAX_VALUE,可看做是无限大。
- Executors.newFixedThreadPool()
创建一个固定大小的线程池,该方法可指定线程池的固定大小,对于超出的线程会在LinkedBlockingQueue队列中等待。
- Executors.newSingleThreadPool()
创建一个只有线程的线程池,该方法无参数,所有任务都保存队列LinkedBlockingQueue中,等待唯一的单线程来执行任务,并保证所有任务按照指定顺序(FIFO或优先级)执行。
- Executors.newScheduledThreadPool()
创建一个可定时执行或周期执行任务的线程池,该方法可指定线程池的核心线程个数。
线程池的工作状态
线程池的状态用 int
类型的值表示,具体表示形式 Java 线程池 ThreadPoolExecutor 中的位运算操作 一文中有详细讲到。
分别以下几种:
-
RUNNING
可以接收新增的任务,并处理阻塞队列中的任务
-
SHUTDOWN
拒绝新增的任务,但是可以处理阻塞队列中的任务
-
STOP
拒绝新增的任务,并拒绝阻塞队列中的任务,同时会中断正在处理的任务
-
TIDYING
表示所有的任务都执行完了,线程池中活跃的线程数为 0,即将调用 terminated 方法
-
TERMINATED
terminated() 方法调用后的状态
它们的值由小到大依次为:
RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
因此当我们看类似到如下表达式时,就应该知道 ws 的状态是什么。
// ws == RUNNING
if(ws < SHUTDOWN) {
// TODO
}
线程池工作状态切换
-
RUNNING -> SHUTDOWN
当调用 shutdown() 方法,或者间接调用 finalize() 方法时 -
RUNNING or SHUTDOWN -> STOP
当调用 shutdownNow() 方法 -
SHUTDOWN -> TIDYING
当队列和线程池都为空时 -
STOP -> TIDYING
当线程池为空时 -
TIDYING -> TERMINATED
当 terminated() hook 方法执行完毕后
线程池的工作原理
线程池的工作原理要结合它的构造参数来说明。
- 当前线程池的线程个数小于
corePoolSize
时,开启新的核心线程执行 - 如果线程池处于
Running
状态,则添加任务到阻塞队列workQueue
- 如果阻塞队列
workQueue
满了,就新增一个非核心线程新增(失败了就执行具体的拒绝策略)。
大致流程入如下:
Worker 内部类
说 Worker 类之前,先来复习下如何创建并启动一个线程。
// 1. 实现 Runnbale 接口
class MyTask implements Runnable {
// 2. 重写 run 方法
void run(){
// doSomething
}
}
// 3. new 一个 Thread,把 MyTask 对象作为参数传进去
Thread thread = new Thread(new MyTask());
// 4. 调用 start() 方法, 最终会去调用 MyTask#run() 方法
thread.start();
而我们的 Worker
类的作用跟上面的 MyTask
是一样的,只不过它的内容更丰富, 来看下 Worker
类的定义。
class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread;
Runnable firstTask;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 省略部分代码
}
-
继承自 AQS 类(可以先忽略)
简单实现了一个不可重入的独占锁,
state=0
表示锁未被占用,state=1
表示锁被占用,默认状态state=-1
(AQS 前面有说到过,这一点也可以暂时忽略。)对 AQS 感兴趣的可以翻阅前面的文章
ReentrantLock 源码分析 - AbstractQueuedSynchronizer 详解(一)
ReentrantLock 源码分析 - AbstractQueuedSynchronizer 详解(二) -
实现了
Runnable
接口Worker
类似上面的MyTask
, 是具体承载任务的对象。 -
thread
属性具体执行任务的线程
-
firstTask
属性记录
Worker
执行的第一个任务
大概讲一下 Worker
在线程池中的作用,把上面几个点串起来:
// 1. new 一个上面定义的 task
MyTask mytask = new MyTask();
// 2. 创建一个 Worker 对象
// 2.1 把 mytask 赋值给了 firstTask 属性
// 2.2 通过线程工厂创建一个 Thread 对象,赋值给 thread
// 2.3 thread 构造参数为 this,也就是 worker(因为 Worker 也实现了 Runnable 接口)
Worker worker = new Worker(mytask);
// 3. 拿到 worker 对象中的 thread 对象
Thread thread = worker.thread;
// 4. 启动一个线程,最终会调用 worker 对象的 run 方法
thread.start();
而我们的 worker#run
方法是个死循环,它主要做的其中一件事情就是执行 firstTask
的 run
方法,另一件事情就是执行阻塞队列 workQueue
中的任务。
具体如何执行的后面会结合源码深入了解。
总结
本文作为线程池源码分析的基础篇部分,主要内容是对 线程池的工作原理进行初步分析,该部分内容暂时讲得比较浅,后面 具体源码分析 时会深入探究。
除此之外,穿插了以下内容:
ThreadPoolExecutor
的构造参数- 线程池的 几种创建方式
- 线程池的 几种工作状态
- 线程池工作状态切换
- 线程池内部类· Worker 的简单介绍