线程池(Thread Pool) 把一个或多个线程通过统一的方式进行调度和重复使用的技术(采用池化思想)。
优点:
1.降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2.提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
3.提高线程的可管理性
在类图中,有两个核心的类。ThreadPoolExector和 ScheduledThreadPoolExecutor。后者不仅可以执行某个任务,还可以周期性地执行任务。向线程池中提交的每个任务,都必须实现Runnable接口。通过最上面的Executor 接口中的execute(Ruunable task)向线程池提交任务。同时,在ExecutorService中,定义了线程池的关闭接口shutdown。还定义了可以有返回值的任务。也就是Callable。
public class ThreadPoolExecutor extends AbstractExecutorService {
//状态变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//存放任务的阻塞队列
private final BlockingQueue<Runnable> workQueue;
//对线程池内部各种变量进行互斥访问控制
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition();
//线程集合
private final HashSet<Worker> workers = new HashSet<Worker>();
......
}
每一个线程是一个Worker对象。Worker继承自AQS。具有锁的一些特性,对于完成线程池的关闭和执行任务起到关键作用。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
//Worker 封装的线程
final Thread thread;
/** Initial task to run. Possibly null. */
// Worker 接受到的第一个任务
Runnable firstTask;
/** Per-thread task counter */
//Worker 执行完毕的任务个数
volatile long completedTasks;
.....
}
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;
}
面试常考:
corePoolSize:在线程池中始终维护的线程个数。
maxPoolSize:在corePoolSize 已满,队列也满的情况下,扩充线程到该值。
keepAliveTime/TimeUnit: maxPoolSize 中的空闲线程,销毁所需要的时间。总线程回收至corePoolSize.
TimeUnit 是时间单位。
blockingQueue:线程池所用的队列类型。一定要设置队列大小。
threadFactory: 线程创建工厂,可以自定义。一般我们采用默认值。
RejectedExecutionHandler:corePoolSize 已满,队列已满,maxPoolSize已满,最后的拒绝策略。
首先是判断corePoolSize,其次判断blockingQueue是否已满,接着判断maxPoolSize.最后使用拒绝策略。所以 一定要设置blockingQueue的大小。不然会一直往队列中塞任务,撑爆服务器内存然后宕机。
线程池的关闭对比于线程的关闭,更加复杂。 比如 当关闭一个线程池的时候,有的线程正在执行某个任务,有的调用者正在向线程池提交任务,并且队列中还有未执行的任务。因此,关闭过程不可能是立刻马上关闭,需要有一个平滑的过渡,这里就涉及线程池的完整生命周期管理。
线程池中,把线程数量(workCount)和线程池状态(runState)这两个变量打包存储在一个字段里,即ctl变量。如下图,最高位的三位存储线程池状态,其余29位存储线程个数。在jdk6中,这两个变量是分开存储。
//初始化时,线程池状态为RUNNING,线程数为0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//最高的三位表示线程池的状态
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
//线程池的5钟状态
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
//从ctl 中分别解包出runState和workerCount
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
//rs 即runState,wc即workerCount,两个变量打包成ctl一个变量
private static int ctlOf(int rs, int wc) { return rs | wc; }
从上面的代码可以看出,ctl变量被拆成两半,最高的3位用来表示线程池的状态,低的29位表示线程的个数。线程池的状态有5种。分别是RUNNING,SHUTDOWN,STOP和TERMINATED.
5种状态的迁移过程如图:
线程池有两个关闭函数,shutdown()和shutdownNow(),这两个函数会让线程池切换到不同的状态。在队列为空,线程池也为空之后,进入TIDYING状态。最后执行一个钩子函数terminated(),进入TERMINATED状态。线程池才 “结束生命”。
这里的状态迁移,只能从小到大迁移,不能逆向迁移。
通过上面的分析,我们了解到 线程池的关闭需要一个过程,在调用shutdown 或者shutdownNow之后,线程池并不会立即关闭,接下来需要调用awaitTermination来等待线程池关闭。关闭线程池的正确步骤如下:
调用完 shutdown或者 shutdownNow后。然后循环调用 awaitTermination()方法来判断线程池的状态。
executor.shutdown();
//调用shutdown()后,调用 awaitTermination
try{
boolean loop=true;
do{
loop=!executor.awaitTermination(2,TimeUnit.SECONDS);
//阻塞,直到线程池里所有的任务结束
}while(loop)
}catch(InterruptedException e){
}
awaitTermination 函数
不断循环判断线程池是否达到了最终状态TERMINATED,如果达到了,就返回,如果不是,则通过termination条件变量阻塞一段时间。"苏醒"之后继续判断。
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//不断循环
for (;;) {
//判断线程池的状态是否是TERMINATED
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
//如果不是,进行阻塞
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
shutdown 和shutdownNow的区别
1.前者不会清空任务队列,会等待所有任务执行完成,后者会直接清空队列
2.前者只会中断空闲的线程,后者会中断所有的线程。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//判断是否有权限关闭线程池
checkShutdownAccess();
//设置状态为SHUTDOWN
advanceRunState(SHUTDOWN);
//只中断空闲的线程
interruptIdleWorkers();
//钩子函数,目前是空的,为自定义的线程池个性化扩展
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//判断是否有权限关闭线程池
checkShutdownAccess();
//设置状态为STOP
advanceRunState(STOP);
//中断所有线程
interruptWorkers();
//清空队列
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
继续分析下 interruptIdleWorkers()中断空闲线程和 interruptWorkers()中断所有线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
//状态是是非interrutped,并且能拿到锁。work也是继承AQS,说明是空闲的
//此时才执行interupt,中断信号
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
void interruptIfStarted() {
Thread t;
//只要线程启动,并且状态不是interrupted。就执行中断信号
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
shutdown和shutdownNow 最后都执行了tryTerminate();
tryTerminate不会强行终止线程池,只是做了检测。当 workerCount为0,workerQueue为空时,先把状切换为TIDYING。然后调用钩子函数terminated,随后把状态改成TERMINATED。最后执行 termination.sinaAll().通知前面阻塞在awaitTermination的所有调用线程。
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
//当workQueue为空,workCount为0时,才会到这
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//cas 修改状态为 TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//调用钩子函数
terminated();
} finally {
//设置状态为 TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
//唤醒所有线程--在awaitTermination
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
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的值
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 (!addWorker(command, false))
//创建线程失败,说明超过最大线程池数了,采用拒绝策略
reject(command);
}
addWorker 此函数用于开一个新的线程,如果第二个参数core为true,则用corePoolSize作为上界,如果为false,则用maxPoolSize作为上界。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//如果线程池的状态为SHUTDOWN,说明线程池进入了关闭过程
// 并且 1. 线程状态已经大于 SHUTDOWN 就直接返回 false,
// 2.新加的任务 不是null.也不能加任务了,则直接返回 false
// 3. 队列是空的,也不加任务了,直接返回 false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
//线程数超过了上界,则不会创建,直接返回false;
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//worker加一,则跳出循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//重新判断c.如果值不相等,说明有其他线程增加了进来。则需要从新循环
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//到这里说明worker加1了,后面的逻辑就是具体实例化一个worker.
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//进行初始化
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
//再次校验rs 的值。即线程池的状态
//线程池的状态还是 RUNNING,
//如果是 SHUTDOWN.可以加null任务
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
//线程还没启动,此时是alive().则抛出异常
throw new IllegalThreadStateException();
//把线程加入到线程集合
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//此时才启动该线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
//如果加入线程失败。则把workerCount减一
addWorkerFailed(w);
}
return workerStarted;
}
在上面的任务提交过程中,可能会开启一个新的Worker,并把任务本身作为firstTask赋给该Worker。但对于一个Worker来说,不是只执行一个任务,而是源源不断地从队列中取任务执行,这是一个不断循环的过程。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
//对于的线程
final Thread thread;
/** Initial task to run. Possibly null. */
//对于的任务
Runnable firstTask;
/** Per-thread task counter */
//统计线程完成的任务数量
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
//初始值设置的是-1
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
//调用的是ThreadPoolExecutor 的runWorker方法
runWorker(this);
}
}
runWorker
//运行的任务有两种类型 一种是线程数不到核心线程数或者队列满了线程数不到最大线程数,直接new Work执行
// 从队列中取
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 从worker中取的任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//不断循环从队列中获取任务执行
//task 首先从work中取,取不到然后再从队列中取(getTask())
while (task != null || (task = getTask()) != null) {
//执行任务先加锁。此处对应了shutdown 来判断线程是否是空闲时的操作tryLock.
//如果能获取到锁,说明没有任务执行,线程是空闲的。
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
//对线程池和线程状态的校验判断,
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
//不符合就interupt,给自己发送中断信号
wt.interrupt();
try {
//任务之前的钩子函数,目前是空
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行任务
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//任务执行完成后的钩子函数,目前是空
afterExecute(task, thrown);
}
} finally {
task = null;
//完成任务数加1
w.completedTasks++;
//释放锁
w.unlock();
}
}
//判断是正常退出还是异常退出,用于finally里面里面进行worker退出的逻辑处理
completedAbruptly = false;
} finally {
//worker退出
processWorkerExit(w, completedAbruptly);
}
}
把任务的执行过程和上面的线程池的关闭过程结合起来进行分析。当调用shutdown()的时候,可能会出现几种情况
场景一:
当调用shutdown时候,所有线程都处于空闲状态。
这意味着任务队列一定是空的。此时,所有线程都会阻塞在getTask()的地方,然后,所有线程都会收到interruptIdleWorkers()发过来的中断信号。getTask()会响应中断,然后返回null.所有Worker都会退出while循环,然后执行processWorkerExit;
场景二:
当调用shutdown时,所有线程都处于忙碌状态
此时队列可能为空,也可能是非空的,interruptIdleWorkers()内部的tryLock调用失败。什么都不会做,直至队列任务为空。interruptIdleWorkers()内部的tryLock调用成功。此时和场景一一样。
场景三:
当调用shutdown时,部分线程忙碌,部分线程空闲。
有部分线程空闲,说明队列是空的。忙碌的线程按照场景一处理,不忙碌的线程按照场景一处理。
getTask()函数
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//rs >= SHUTDOWN 说明调用了shutdown。
// 并且队列为空 或者 rs >= STOP 调用了shutdownNow ,则返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//1.队列为空,就会阻塞在此处的poll或者take。poll 带超时时间。take不带超时时间。
//2.一旦收到中断信号,就会进入catch代码,设置 timedOut=false;
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
//可以响应 interruptIdleWorkers 发出的中断信号
timedOut = false;
}
}
}
shutdownNow()比较粗暴。和shutdown()相比,多了一个清除队列的操作。少了一些判断而已。
整体来讲,线程池的设计还是比较复杂的。本文首先从整体架构来介绍了线程池,然后逐步从线程池的提交执行和关闭等操作进行了源码分析。希望能对大家理解线程池有所帮助。