解读Java线程池
前言
总结学习下线程池的内部逻辑
继承关系
graph BT a(ExecutorService)-->b(Executor) c(AbstractExecutorService)-->a d(ThreadPoolExecutor)-->c
ThreadPoolExcutor
- corePoolSize
核心线程数
- maximumPoolSize
最大线程数,线程池允许创建的最大线程数
- workQueue
任务队列,BlockingQueue接口的某个实现
- keepAliveTime
空闲线程的保活时间,如果某线程的空闲时间超过这个,那么可以被关闭了。注意这个值并不好对所有的线程起作用,如果线程池中的线程数少于等于核心线程数corePoolSize,那么这些线程不会因为空闲太长时间而被关闭,当然也可以通过调用allowCoreThreadZTimeOut(true)使核心线程数内的线程也可以被回收。
- threadFactory
用于生成线程的,一般用来给该线程池中的线程取个容易区分的name。
- handler
当池中线程达到最大线程数,同时等待队列也已满了。这时再来任务就会被拒绝,并调用handler的rejectedExecution()方法
Doug Lea采用一个32位的整数来存放线程池的状态和当前池中线程数,高3位用于存状态,低29位表示线程数(最多大概5亿多个线程)// 将整数c的低29位修改为0,就得到了线程池的状态 private static int runStateOf(int c) { return c & ~CAPACITY; } // 将整数c的高三位修改为0,就得到了线程池中线程数 private static int workerCountOf(int c) { return c & CAPACITY; } private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池的状态
- Running:接收新的任务,处理等待队列中的任务
- Shutdown:不接受新的任务提交,但是会继续处理等待队列中的任务
- Stop:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程
- Tidying:所有任务都销毁了,workCount为0.线程池的状态转换为Tidying时,会执行钩子方法terminated()
- Terminated:terminated()方法结束后,线程池就是这个状态了。
Running定义为-1,shutdown定义为0,其他的都比0大,所以等于0的时候不能提交任务,大于0的话,连正在执行的任务也需要中断。
状态转换
- Running -> Shutdown:当调用了shutdown()后,会发生这种状态转换。
- (Running or Shutdown) —> Stop:当调用ShutdownNow()后,会发生这种转换。
- Shutdown->Tidying:当任务队列和线程池都清空后,会由Shutdown转换为Tidying
- Stop->Tidying:当任务队列清空后,发生这个转换。
- Tidying->Terminated:当terminated()方法结束后
Worker
继承于AbstractQueuedSynchronizer,对就是继承于大名鼎鼎的AQS,因为涉及到了多线程操作,就存在同步互斥问题,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. */
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) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
// 如果当前线程数小于核心线程数,那么直接添加一个worker来执行任务。创建一个新的线程,并把当前任务command作为这个线程的第一个任务(firstTask)
if (workerCountOf(c) < corePoolSize) {
// 如果添加成功那么久结束,返回false代表线程池不允许提交任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 到这说明要么当前线程数打印等于核心线程数,要么刚刚addWorker失败。如果线程池处于Running状态,把这个任务添加到workQueue中
if (isRunning(c) && workQueue.offer(command)) {
/* 这里说明任务进入了workQueue,我们是否需要开启新的线程
线程数在[0,corePoolSize)是无条件开启新的线程
如果线程数已经大于等于corePoolSize,那么将任务添加到队列中然后进行到这里
*/
int recheck = ctl.get();
// 如果池不再运行,则移除任务并且执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果池还是Running的,并且线程数==0,开启新线程。
// case:提交任务到队列了,但是线程都关闭了。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 若workQueue队列满了,那么以maximumPoolSize为界创建新的worker,如果失败说明线程数已经达到maximumPoolSize,执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
addWorker(Runnable firstTask, boolean core)
第一个参数是准备提交给这个线程执行的任务,可以为null,第二个参数true表示使用核心线程数corePoolSize作为创建线程的界限,也就说创建这个线程的时候若线程池中的线程总数已经达到corePoolSize那么不能响应本次创建,如果为false表示以最大线程数maximumPoolSize作为界限。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 若线程池已关闭,并满足下条件之一,那么不创建新的worker:1. 线程池状态大于Shutdown,也就是Stop、Tidying、Terminated。2.firstTask != null。3.workQueue.isEmpty()
// 简单分析如下:还是状态控制的问题,当线程池处于Shutdown时,不允许提交任务,但已有的任务继续执行,当状态大于Shutdown时,不允许提交任务且中断正在执行的任务。
// case: 当线程池处于Shutdown,但是firstTask为null且workQueue非空,那么是允许创建worker的。这是因为Shutdown的语言是:不需要提交新的任务,但是要把已经进入到workQueue的任务执行完。
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 如果成功,那么就是所以创建线程的条件校验都满足了,准备创建线程执行任务了。如果失败的话,说明有其他线程叶子尝试往线程池中创建线程
if (compareAndIncrementWorkerCount(c))
break retry;
// 由于有并发,重新再读取一些ctl
c = ctl.get(); // Re-read ctl
// 正常如果是CAS失败的话,进到下一个里层的for循环就可以了。如果是因为其他线程的操作导致线程池状态变更,如有其他线程关闭线程池,那么需要回到外层for循环
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// 到这里可以开始创建线程来执行任务了。
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());
// 小于Shutdown就是Running状态,等于Shutdown不再接收新任务,但是会继续执行等待队列中的任务。
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// worker中的线程可不能提前开启了
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 加到全局workers(HashSet)中
workers.add(w);
int s = workers.size();
// largestPoolSize用于记录workers中的个数最大值。
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 添加成功了,才能启动线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 没添加成功就做一些清理工作。
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
// 失败就删除对应worker,workcount减1
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
worker中线程start后就值run方法调用runWorker方法。
public void run() {
runWorker(this);
}
// worker中线程启动后,while循环不断从等待队列中获取任务并执行。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 循环调用getTask()
while (task != null || (task = getTask()) != null) {
单个worker的锁
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
// 如果线程池状态大于等于stop那么当前线程也要中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 子类可以实现,用于在task.run()之前调用
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,准备getTask下一任务
task = null;
// 累计完成数+1
w.completedTasks++;
// 释放当前worker独占锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 1.执行到这说明getTask返回null,也就是队列中已经没有任务需要执行了,执行关闭。
// 2.任务执行过程中出现异常
// 第一种已经在代码处理了workCount减一,这个在getTask中。
// 第二种情况,workCount没有进行处理,所以需要在processWorkerExit中处理。
processWorkerExit(w, completedAbruptly);
}
}
getTask()获取任务
此方法有三种可能:
- 阻塞直到获取任务返回,默认corePoolSize之内的线程不会被回收,它们会一直等待任务。
- 超时退出。keepAliveTime起作用的时候,也就是如果这么多时间内都没有任务,那么应该执行关闭。
- 如果发生以下条件,此方法必须返回null,用于关闭该线程。
- 池中有大于maximumPoolSize个workers存在(通过调用setMaximumPoolSize进行设置)
- 线程池处于Shutdown,而且workQueue是空的,这种不接受新的任务
- 线程池处于stop,不仅不接受新的线程,连workQueue中的线程也不再执行。
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.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// cas操作减少工作线程
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 允许核心线程数内的线程回收或线程数超过了核心线程数,就会超时关闭
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 线程数大于maximumPoolSize或者超时并且线程大于1或工作队列为空,都可能返回null
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
// cas失败,就继续for循环
continue;
}
// wc <= maximumPoolSize同时没有超时
try {
// 取任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
// 如果worker发生了中断,采取的方案是重试
// 为什么会发生中断:可以看下setMaximumPoolSize方法
// 如果开发者将maximumPoolSize调小了,导致其小于当前workers数量,那么意味着超出的部分线程要被关闭,重新进入for循环自然有部分返回null
timedOut = false;
}
}
}
拒绝策略
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
这里的handler
就是构造线程池时传入的参数,继承于RejectedExecutionHandler
。在ThreadPoolExecutor
中有四个已经定义好的实现类供我们直接使用,我们也可以自己实现。
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
// 只要线程池没有关闭,那就交由提交任务的线程自己执行这个任务。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
// 直接抛出RejectedExecutionException异常,这是默认的策略
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
// 不做处理,直接忽略
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
// 若线程池没有关闭,那么把队列对头的任务直接扔掉,然后提交这个任务到等待队列中
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}