先看一段代码,来想俩个问题
带着这俩个问题看源码,事半功倍!
- 为什么会报错,而且是第四个报错?
- 为什么第三个先执行,第二个后执行?
构造方法分析
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;
}
- 在构造方法中,只做了参数的校验,没有做任何的逻辑处理!
- ThreadPoolExecutor有7个参数,下面分别介绍一下。
- corePoolSize:核心线程数,也是线程池中常驻的线程数。注意:线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
- maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程。简单理解就是临时的线程。
- keepAliveTime:非核心线程的存在时间。非核心线程的空闲时间超过这个时间,非核心线程就会被回收。如果corePoolSize = maximumPoolSize的时候,不存在非核心线程,所以也不存在回收!
- unit:keepAliveTime的时间单位。
- workQueue:用于保存任务的队列。当任务的数量超过了maximumPoolSize,这时候新的任务就会被存放在这个队列中。
- threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建。
- handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。简单理解就是maximumPoolSize满了放在队列中,队列也满了的时候,应该如何处理!
线程池执行:execute()方法分析
public void execute(Runnable command) {
// 任务为空,直接抛出空指针异常!
if (command == null)
throw new NullPointerException();
// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
int c = ctl.get();
// 判断当前的线程数是否小于corePoolSize。这里处理的是核心线程数,对应上面的代码就是线程1。
if (workerCountOf(c) < corePoolSize) {
// 使用入参任务通过addWord方法创建一个新的线程
if (addWorker(command, true))
// 线程能创建成功,说明已经执行完成!直接返回
return;
// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
c = ctl.get();
}
// 如果在上面任务没有创建线程,状态是运行的,就会将当前任务放在队列中
if (isRunning(c) && workQueue.offer(command)) {
// 放入队列成功,再次获取当前的线程池数据
int recheck = ctl.get();
// 再进行一次check,如果状态在任务加入队列后变为了非运行(有可能是在执行到这里线程池shutdown了),需要把当前任务移除掉。
if (! isRunning(recheck) && remove(command))
// 非运行状态并且移除成功,执行拒绝策略。
reject(command);
// 判断当前工线程池有效线程数量是否为0
else if (workerCountOf(recheck) == 0)
// 增加工作者,空的任务,非核心线程!保证线程池在running状态必须有一个任务在执行
addWorker(null, false);
}
// 核心线程用完,不能增加到队列。然后去增加非核心线程数!
else if (!addWorker(command, false))
// 增加非核心线程数失败,说明队列满了,线程也满了。调用拒绝方法
reject(command);
}
- 看到这里其实就能解释,为什么第四个报错!第一个放在核心线程里面,第二个放在队列,第三个在非核心线程,第四个调用了reject方法。
线程池有哪些状态?
// runState is stored in the high-order bits
// 高三位表示 线程池状态
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;
执行的时候多次调用了:addWorker()方法
private boolean addWorker(Runnable firstTask, boolean core) {
// 一个循环的标识,用于跳出循环,java不推荐使用的。
retry:
for (;;) {
// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
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);
// 当前工线程池有效线程数量 大于 线程最大数量(一个最大值,后29位的最大值)
// 或者 工线程池有效线程数量 大于 核心(总)线程最大数量 的时候,不能继续执行!
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 通过CAS(后续有详细文章介绍CSA)操作,将线程数量加一。成功跳出循环!
if (compareAndIncrementWorkerCount(c))
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由于workerCount更改而失败;重试内部循环
}
}
// 上面的代码主要是为了cas将线程数加一
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 一个内部类:通过threadfactory去创建线程!
w = new Worker(firstTask);
// 得到你这个Worker的线程
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 在ThreadFactory出现故障或
// shut down before lock acquired. 在获得锁之前关闭。
// 获取的线程池的状态
int rs = runStateOf(ctl.get());
// 小于shutdown就是running状态
// SHUTDOWN的时候firstTask为空。是从队列中处理任务!那就可以放到集合中
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// precheck that t is startable 预先检查t是否可启动
// 发生的情况就是:线程还没start
if (t.isAlive())
throw new IllegalThreadStateException();
// workers是一个HashSet,将该worker对象添加其中
workers.add(w);
// 获取当前HashSet的长度
int s = workers.size();
// 长度比较大。记录最大线程数
if (s > largestPoolSize)
largestPoolSize = s;
// 更新状态:说明线程可以被启动了
workerAdded = true;
}
} finally {
// 释放锁!
mainLock.unlock();
}
// 上面创建成功,标识线程可以启动
if (workerAdded) {
// 线程启动
t.start();
// 说明线程已经启动了
workerStarted = true;
}
}
} finally {
// 线程启动失败了,处理失败逻辑
if (! workerStarted)
// 失败回退 从wokers移除w 线程数减1 尝试结束线程池
addWorkerFailed(w);
}
return workerStarted;
}
再了解下刚才用到的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. */
// 正在运行woker线程
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);
}
线程池中线程的运行逻辑
- 在上面代码addWorker方法中,有 t.start(); 这么一行,这一行便是开启了真正的线程
- 这一行代码开启了新的线程,并且调用了调用的是Worker的run()方法。
- 然后Worker的run()方法调用了 runWorker(this);
线程的执行:runWorker(this)
final void runWorker(Worker w) {
// 获取当前的线程
Thread wt = Thread.currentThread();
// 获取当前的任务
Runnable task = w.firstTask;
// 设置当前的任务为null
w.firstTask = null;
// 解锁操作,对应Worker类初始化的 setState(-1);
w.unlock(); // allow interrupts
// 是否因为异常退出循环
boolean completedAbruptly = true;
try {
// 当前有任务,直接执行
// 当前无任务,从队列中获取任务执行!
// 这里能看出来第二个问题的答案,为什么先执行3,后执行2
while (task != null || (task = getTask()) != null) {
// 加锁
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语句期间可能也执行了shutdownNow方法,shutdownNow方法会把状态设置为STOP
// Thread.interrupted()方法的作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。
// 当前线程大于等于stop就中断,或者在Thread.interrupted()之后可以满足,并且他不是被中断的状态
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
// 中断当前线程
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 设置为空,等下次循环就会从队列里面获取
task = null;
// 完成任务数+1
w.completedTasks++;
// 解锁
w.unlock();
}
}
// 标志不是因为异常退出
completedAbruptly = false;
} finally {
// 任务执行完成的处理逻辑
processWorkerExit(w, completedAbruptly);
}
}
如何从队列中获取任务:getTask()
private Runnable getTask() {
// 定义超时的标志:上次从阻塞队列中取任务时是否超时
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
int c = ctl.get();
// 获取的线程池的状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.仅在必要时检查队列是否为空。
// 当前状态大于等于SHUTDOWN并且(大于等于STOP或者队列为空)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 线程已经停止,当前的workerCount减一。为了抵消addWorker中的compareAndIncrementWorkerCount(c)的方法的加一
decrementWorkerCount();
return null;
}
// 得到当前工线程池有效线程数量
int wc = workerCountOf(c);
// Are workers subject to culling? 工人会被淘汰吗?
// 如果为false(默认),则核心线程即使在空闲时也保持活动状态。如果为true,则核心线程使用keepAliveTime超时等待工作。
// wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;对于超过核心线程数量的这些线程,需要进行超时控制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 当前工线程池有效线程数量 大于 要求的最大线程数
// timed && timedOut 如果为true,表示当前操作需要进行超时控制,并且上次从阻塞队列中获取任务发生了超时.
// 有效线程数量大于1,或者阻塞队列是空的.
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 那么尝试将workerCount减一。为了抵消addWorker中的compareAndIncrementWorkerCount(c)的方法的加一
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 如果为true,则通过阻塞队列的poll方法进行超时控制,如果在keepAliveTime时间内没有获取到任务,则返回null;
// 否则通过take方法,如果这时队列为空,则take方法会阻塞直到队列不为空。
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 获取到队列的数据,直接返回
if (r != null)
return r;
// 没有获取到数据,说明已经超时,timedOut设置为true
timedOut = true;
} catch (InterruptedException retry) {
// 如果获取任务时当前线程发生了中断,则设置timedOut为false并返回循环重试
timedOut = false;
}
}
}
任务执行完做什么:processWorkerExit()
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果completedAbruptly为true,说明发生了一次
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
// 仔细减一操作
decrementWorkerCount();
// 获取说
final ReentrantLock mainLock = this.mainLock;
// 加锁
mainLock.lock();
try {
// 每个worker线程执行完成的任务数
completedTaskCount += w.completedTasks;
// 移除当前的工作任务
workers.remove(w);
} finally {
// 解锁
mainLock.unlock();
}
// 根据线程池状态进行判断是否结束线程池
tryTerminate();
// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
int c = ctl.get();
// 当前状态小于STOP状态
if (runStateLessThan(c, STOP)) {
// 不是异常中断
if (!completedAbruptly) {
// 如果为false(默认),则核心线程即使在空闲时也保持活动状态。如果为true,则核心线程使用keepAliveTime超时等待工作。
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 至少保留一个Worker的逻辑
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 当前的工作数量大于等于核心线程数量
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 继续执行新的任务,空任务,从队列获取!
addWorker(null, false);
}
}
线程池什么时候会结束:tryTerminate();
final void tryTerminate() {
for (;;) {
// 获取线程池的数据:高 3 位 表示线程池状态,低 29 位表示线程个数
int c = ctl.get();
// RUNNING,因为还在运行中,不能停止;
// TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;其他限制正在整理或者已经停止。
// HUTDOWN并且等待队列非空,这时要执行完workQueue中的task。
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 如果线程数量不为0
if (workerCountOf(c) != 0) { // Eligible to terminate
// 中断一个空闲的工作线程
interruptIdleWorkers(ONLY_ONE);
return;
}
// 获取锁
final ReentrantLock mainLock = this.mainLock;
// 加锁
mainLock.lock();
try {
// 尝试设置状态为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 失败后CAS重试
}
}
结束语
- 获取更多有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你逐步对MySQL以及并发编程有更深入的理解!
- 这个公众号,无广告!!!每日更新!!!