文章目录
- 1.类结构
- 2. 线程池流程和execute方法
- 2.1 addWorKer()方法
- 2.2 Work 的runWorker方法
- 3. 线程池空闲线程的释放
- 4. 线程池的关闭
- 4.1 shutdown和shutdownNow
- 4.2 共同方法:tryTerminate
1.类结构
- Executor接口。该接口定义执行器执行方法execute。
- ExecutorService接口。该接口定义管理执行器的一些方法。
- 抽象类AbstractExecutorService默认实现了ExecutorService执行器执行的方法(submit 和invoke**)。
- ThreadPoolExecutor类继承抽象类,实现所有接口的方法。使用BlockingQueue队列提供任务排队等待功能。并提供4种拒绝策略(当队列满,线程池也满的时候调用,分别是 DiscardPolicy:直接抛弃任务;AbortPolicy:拒绝并抛出异常;DiscardOldestPolicy:移除等待队头任务,当前任务入队;CallerRunsPolicy: 直接调用任务run方法。默认使用AbortPolicy)。
2. 线程池流程和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; // sucess
c = ctl.get(); // 重新获取(1.执行时线程池被关闭;2.workerCountOf(c) >=corePoolSize)被其他线程抢先执行)
}
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); // 以最大线程比较,创建新的线程(因为传入的时null,接下来会从队列中获取任务)
}
else if (!addWorker(command, false)) // 线程池已经关闭了或者队列满了(尝试新建线程)
reject(command); // 缓存队列满,最大线程数满-->拒绝任务
}
2.1 addWorKer()方法
这个方法返回是否执行了传入的firstTask:
False:
1. 线程池已经关闭,或者不是线程池被关闭task为空并且队列不为空的情况(这里常常指当工作线程不够完成队列的任务时,调用 addWorker(null, false)的时候,线程池被关闭。这个时候如果队列已经为空了就没有必要新建线程去完成任务了)。
2. 根据core参数。判断出当前刚工作线程数量已经大于核心线程数量或者最大线程数量
True:
1. work加入到set集合中,并且work线程被启动。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 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)) // CAS ctl+1,跳出循环,进行后续
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs) // 重读发现获取的值不一样了,需要重新从外循环执行
continue retry;
// else CAS failed due to workerCount change; retry inner loop // 继续CAS操作
}
}
---------------------------------------------------------------------------------------------
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); // 构建work
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()); // 检查线程工厂出错或者加锁之前线程池被关闭
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) { // 只有当线程池再运行状态或者线程池已经关闭,但是队列中还有任务
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); // 添加到set集合中
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true; // 添加成功
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // work线程工作
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); // 从set集合中移除work,并运行线程-1
}
return workerStarted;
}
2.2 Work 的runWorker方法
work被启动调用的方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts // new Worker()是state=-1,此处是调用Worker类的tryRelease()方法,将state置为0, 而interruptIfStarted()中只有state>=0才允许调用中断,shutdown中中断也需要先trylock(),下面的w.lock前提也是state从-1变为0
boolean completedAbruptly = true; // 异常完成
try {
while (task != null || (task = getTask()) != null) { // fistTask不为空,或者队列不为空(队列空,指定的空闲线程会被回收)
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())
wt.interrupt();
try {
beforeExecute(wt, task); // hook
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); // hook
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); // 处理work的退出
}
}
3. 线程池空闲线程的释放
当队列为空的时候,线程池会对空闲线程释放。而核心线程数没设置超时的话,核心线程池的work线程就take()阻塞来保持核心线程数,直到线程池被关闭或者队列不为空继续工作。来看一下代码:
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())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c); // 获取工作线程数
// 释放线程
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 是否需要释放线程(true:当设置了允许核心线程数过期,或者当当前工作线程数>核心线程数)
if ((wc > maximumPoolSize || (timed && timedOut)) // 满足两个条件释放线程:
&& (wc > 1 || workQueue.isEmpty())) { 1:工作线程数大于最大线程池数或者需要timed为true,并且当前线程过了keepAliveTime的时间 ;2:存在工作线程或者队列为空(因为可能这期间又有任务入队列了,这个时候如果存在的工作线程数>1,可以继续释放,不会影响任务执行。
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take(); // 需要空闲线程释放,利用阻塞队列的超时poll来等待空闲线程存活时间。如果在指定时间无法获取到队列中的任务,说明该线程可以被回收(timedOut=true)。当不需要释放空闲线程,即timed=false,用take()阻塞来保持核心线程
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
4. 线程池的关闭
4.1 shutdown和shutdownNow
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess(); // 检查权限
advanceRunState(SHUTDOWN); // CAS state--->SHUTDOWN,只改变高三位
interruptIdleWorkers(); // 中断set集合中的所有的空闲的工作线程(正在获取task)
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();// 尝试结束程序
}
interruptIdleWorkers会遍历工作线程,先获取每一个work的lock,才去中断。也就是说
1. 只有还没开始真正执行任务,在获取执行的任务过程中的work线程才会响应中断
2. 对于没有响应线程中断的work继续执行当前线程,完成后正常去执行阻塞队列的任务
所以说shutdown关闭线程池很平滑,安全。
相比shutDown,shutDown比较暴力一点。它会直接移除阻塞队列中所有的任务,尝试关闭所有的工作线程,所以响应性能更好一下。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess(); // 检测权限
advanceRunState(STOP); // CAS state--->STOP
interruptWorkers();// 中断set集合中的所有的工作线程(在runTask中w.unlock()之后的所有工作线程)
tasks = drainQueue();// 将所有阻塞队列移除到tasks中
} finally {
mainLock.unlock();
}
tryTerminate(); // 尝试终止程序
return tasks;
}
4.2 共同方法:tryTerminate
最后看一下两个方法共同调用的tryTerminate
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) // 还是运行状态|已经关闭状态|关 闭状态但是队列不为空(当每一个work退出的时候会调用processWorkerExit(w,completedAbruptly)方法,其内部也会调用tryTerminate方法)
return;
// 1.state->shutdown ,队列一定为空;说明有空闲线程堵塞取任务
// 2.state->stop,队列一定为空;说明有空闲线程堵塞取任务
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE); // 这个时候还有工作线程,中断空闲的(阻塞),后续 进入processWorkerExit方法
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // state -> TIDTING
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0)); // state->TERMINATED
termination.signalAll(); // 唤醒
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}