关于ThreadPoolExecutor调用了submit之后发生了什么
文章目录
如果只想看详细的执行流程,建议直接看最后一小节,同时您可以打开编译器,对比着源码
同时您需要了解一点可重入锁ReentrantLock的使用方法,了解线程池的生命周期和线程的生命周期
前置的杂乱知识点
Executors的基本特点
- 你不需要创建任何Thread,如果你想要执行一个并发的任务,你只需要创建一个任务的实例,并且将它传送给executor。Executor将会管理线程并且执行任务。
- Executors通过重复利用线程来减少创建线程的消耗,实际上,executors通过管理一个名叫
worker-threads
的线程池。如果你将一个任务传送至executor并且存在一个worker-thread
是空闲的,executor将会使用这个线程执行这个任务。- 你可以限制你的executor内的任务线程
work-threads
的数量,如果你提交了超过work-threads
数量的任务,executor
将会把他们存入队列,当某个任务线程完成了一个任务,这个线程会从任务队列中取出另一个任务进行执行。- 你必须明确地结束一个
executor
的执行。你必须告知executor
它必须完成所有的执行任务并且杀死所有创建的线程。——<Mastering Concurrency Programming with Java 8>
![](https://i-blog.csdnimg.cn/blog_migrate/97594da8180e94ad8de78eae9d2526e4.png)
- 线程池需要支持多个线程并发执行,因此有一个线程集合
Collection<Thread>
,来执行线程任务; - 设计任务的异步执行,因此需要有一个集合来缓存任务队列
Collection<Runnable>
; - 很显然在多个线程之间协调多个任务,那么就需要一个线程安全的任务集合,同时还需要支持阻塞、超时操作,那么
BlockingQueue
就是必不可少的; - 既然是线程池,出发点就是提高系统性能的同时降低资源消耗,那么线程池的大小就有限制,因此需要有一个核心线程池大小和一个最大线程池大小,有一个计数来描述当前线程池大小;
- 如果是有限的线程池大小,那么长时间不使用的线程资源就应该销毁掉,之久需要一个线程空闲时间的计数来描述线程何时被销毁;
- 线程池是有生命周期的,因此需要有一个状态来描述当前线程池的运行状态;
- 线程池的任务队列如果有边界,那么就需要有一个任务拒绝策略来处理过多的任务,同时在线程池的销毁阶段也需要有一个任务拒绝策略来处理新加入的线程;
- 线程池大小、线程空闲时间、线程池运行状态等等状态改变都不是线程安全的,因此需要一个全局锁
mainLock
来协调这些竞争资源; - 除了上述数据结构外,还有一些状态来描述线程池的运行计数,例如线程池运行的任务数、曾经达到的最大线程数,主要用于调试和线程分析。
对于ThreadPoolExecutor
而言,一个线程就是一个Worker
对象,它与一个线程绑定,当Worker
执行完毕就是线程执行完毕。
既然是线程池,首先来研究一下线程的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
static class DefaultThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
final ThreadGroup group;
final AtomicInteger threadNumber = new AtomicInteger(1);
final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
在这个线程工厂中,同一个线程池的所有线程属于同一个线程组,也就是创建线程池的那个线程组,同时线程池的名称都是pool-<poolNum>-thread-<threadNum>
。另外对于线程池中的所有线程都默认转成非后台线程,这样主线程退出时不会退出JVM,而是等待线程池结束。同时默认将线程池中所有的线程都调为同一个级别,这样在操作系统的角度看来所有系统都是公平的,不会导致竞争堆积。
// ThreadPoolExecutor
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//❤newThread中将Worker本身传入,Worker本身实现了Runnable接口
}
public void run() {
runWorker(this);
}
protected boolean isHeldExclusively() {
return getState() != 0;
}
//...
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
//...
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
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 ((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 = null;
w.completedTasks++;
w.unlock();//👈
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
简单逻辑如下
private final class Worker implements Runnable {
private final ReentrantLock runLock = new ReentrantLock();
private Runnable firstTask;
Thread thread;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
}
private void runTask(Runnable task) {
final ReentrantLock runLock = this.runLock;
runLock.lock();
try {
task.run();
} finally {
runLock.unlock();
}
}
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
}
// AbstractExecutorService
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
// ThreadPoolExecutor
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))//👈addWorker
return;
c = ctl.get();
}
//...
reject(command);
}
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
接下来我们看看不同的Handler对应的不同拒绝策略
默认的Handler为AbortPolicy()
。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { }
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
//ThreadPoolExecutor.Worker
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
//来看看default的Factory是啥
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
![](https://i-blog.csdnimg.cn/blog_migrate/f6942658644a0573ca7414d621e66928.png)
看一下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 = 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);
}
首先我们看看ctl的作用是啥
ctl是一个标志量,高三位用来表示当前线程池的状态RUNNING\SHUTDOWN\STOP\TERMINATED
,低29位表示正在运行的线程数量。
private static int runStateOf(int c) { return c & ~CAPACITY; }//线程池的运行状态
private static int workerCountOf(int c) { return c & CAPACITY; }//正在工作的线程数量
private static int ctlOf(int rs, int wc) { return rs | wc; }//将运行状态(高3位)和正在工作的线程数量(低29位)组合起来
addWorker
主要有三种调用方式
addWorker(command, true);
addWorker(null, false);
addWorker(command, false);
- 如果线程池正在运行,并且任务成功添加进任务队列
- 线程池停止运行,将任务移除,执行reject。
- 如果没有正在运行的线程,则调用addWorker,处理任务队列中的任务。
- 如果添加worker失败的话,执行reject策略
reject的简单观察
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
addWorker全解析
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,将WorkerCount加一
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//...//♥③👈
}
我们首先看一下♥①👈
指示的判断
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
rs
表示线程池状态,
线程池运行中可以通过shutdown()和shutdownNow()来改变运行状态。shutdown()是一个平缓的关闭过程,线程池停止接受新的任务,同时等待已经提交的任务执行完毕,包括那些进入队列还没有开始的任务,这时候线程池处于SHUTDOWN状态;shutdownNow()是一个立即关闭过程,线程池停止接受新的任务,同时线程池取消所有执行的任务和已经进入队列但是还没有执行的任务,这时候线程池处于STOP状态。
// runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS;//COUNT_BITS是29位 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
firstTask
就是刚刚我们调用addWorker
传入的command
参数,workerQueue
存储的是目前还没被执行的任务**(注意是任务Runnable,不是线程)**。
-
首先是初始的状态判断
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())) return false;
首先我们要明确一点,SHUTDOWN只是停止接受外面新加入的线程,但是要等待已经提交到任务队列的任务执行完毕
JDK中的注释是这么说的
SHUTDOWN: Don't accept new tasks, but process queued tasks
就像小羊反刍一样,虽然不吃外面的了,但是要把之前吃的处理完
- 如果当前线程池不是
RUNNING
状态(rs≥SHUTDOWN,rs可能为SHUTDOWN\STOP\TIDYING\TERMINATED)
,直接返回false,表示添加失败。 - 当前线程已经关闭(
rs == SHUTDOWN
),并且没有传入任务(firstTask == null
)而且任务队列不为空(!workQueue.isEmpty()
),这种情况是当线程池为SHUTDOWN
的时候可以执行内部堆积任务(存储在workerQueue
内的任务)的条件,如果任何一个条件不成立,那么便不可以执行内部堆积任务,返回false。
- 如果当前线程池不是
-
接下来,如果已经运行着的线程数大于等于限制数量
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))//♥②👈 return false;
- 运行着的线程数大于等于线程池的最大容量( C A P A C I T Y = 1 29 − 1 CAPACITY=1^{29}-1 CAPACITY=129−1)
- 或者如果是核心线程(
core==true
)则判断运行着的线程数量大于等于核心线程数量限制(corePoolSize
) - 或者如果是非核心线程(
core==false
)则判断运行着的线程数量大于等于最大线程数量限制(maximumPoolSize
)
返回false,不能再添加了,已经满了要溢出来了。
-
WorkCount加一操作,是利用的CAS
if (compareAndIncrementWorkerCount(c))//👈CAS,将WorkerCount(包含于ctl中)加一 break retry;
break retry 跳到retry处,且不再进入循环
continue retry 跳到retry处,且再次进入循环
我们接着看省略号的部分♥③👈
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 {
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);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
-
什么情况下我们才能添加worker呢?
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {//♥④👈 if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); //... }
-
要么你线程池是正在运行
-
要么线程池关闭了,那你不可以传入任务了,可以理解为:我厂子快关门了,正在赶紧把之前老客户的订单完成,完成后就关门,你可以催我,但是别给我订单了,不收了,task必须是null。
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null))
-
即使进入了判断体也不要开心,每个worker对应着一个线程thread和一个可执行的任务firstTask,上面的语句是用来判断一个worker能不能成为线程池的一员,也就是说你的线程要运行,必须在判断之后才能运行,但是如果你判断之前就调用了
Thread.start
方法,Thread.isAlive
就是true
,那不行,抛出异常,你一定是在我判断之前就直接调用了Thread.start
。if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException();
-
然后就可以把worker加入到集合workers中了。
-
-
上面都顺利通过了,则调用worker的start方法,开始执行worker内部包含的线程……停
if (workerAdded) { t.start(); workerStarted = true; }
你这个线程,执行的是哪朝哪代的任务?不会是在空转吧?不用担心,每个Worker都是实现了Runnable接口,在Worker构造的时候
//ThreadPoolExecutor.Worker
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
//ThreadPoolExecutor.Worker
public void run() {
runWorker(this);
}
Worker在执行构造方法的时候,把自己给传入了!当上上面的t就是Woker的Thread,执行之后就会调用Worker的run方法,然后调用runWorker方法。
总结并且拓展一下
- 正常情况下,在调用addWorker之后,便会开始执行任务。
- Worker里面有一个Thread和一个Runnable,如果Runnable是null,说明这个线程是用来运行任务队列
workerQueue
内的任务的
runWorker在干嘛?
java中的中断Thread.interrupt()意味着什么?
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
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 ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} //...
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
-
如果worker里面设置的Runnable即
firstTask
不是null,则执行自带的firstTask
,否则调用getTask()
方法从workerQueue
里面获得之前缓存的Runnable任务while (task != null || (task = getTask()) != null)
-
当我们想要执行任务的时候,仍然要检查线程池的状态,你在
她它的池子里随波而动。下面这段就记住,目的是设置线程的中断标志。if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt();
-
接下来调用task.run()
-
finally,我们将运行完的worker移出works集合
总结
-
▲[submit]当我们submit一个Runnable任务,会将这个任务包装成一个
RunnableFuture
,调用execute
-
▲[execute]在
execute
中,如果目前的线程数量小于核心线程数,可以直接调用addWorker
-
▲[execute]如果2调用的
addWorker
失败,没有成功添加并且运行任务,则查看线程池是否正在运行,然后将任务添加到任务队列中3.1 ▲[execute]此时的command(我们传入的Runnable任务)已经添加到任务队列中,我们再次判断线程池是否正在运行
3.1.1▲[execute]如果线程池停止运行,将command从任务队列中抽出,调用reject方法,即采用线程池任务拒绝策略
3.1.2▲[execute]如果线程池还在运行并且运行的线程数量为0,说明我们要往线程池中添加线程,由于运行到这里,说明我们第一次调用
addWorker
的时候失败了,那么这次再次调用addWorker
的时候,第一个参数传入null,因为需要执行的任务已经在任务队列中了,第二个参数传入false,因为之前失败了,这次就别创建核心线程了(核心与否其实没有什么影响),调用addWorker(null, false);
-
▲[execute]如果2调用的
addWorker
失败,并且3中线程池没有运行或者添加任务队列失败,继续调用addWorker
方法,addWorker(command, false);
如果线程池没有运行,addWorker
直接返回false;如果仅仅是添加任务队列失败,任务不能被扔掉,我们仍然要尝试一下能不能运行,第二个参数为false,说明我们不要创建核心线程,目的是保证最大程度上能运行成功。 -
▲[execute]如果4调用的
addWorker
仍然失败,直接执行reject
方法,采用拒绝策略。 -
▲[addWorker]如果当前线程池不是
RUNNING
状态(rs≥SHUTDOWN,rs可能为SHUTDOWN\STOP\TIDYING\TERMINATED)
,直接返回false,表示添加失败。 -
▲[addWorker]当前线程已经关闭(
rs == SHUTDOWN
),并且没有传入任务(firstTask == null
)而且任务队列不为空(!workQueue.isEmpty()
),这种情况是当线程池为SHUTDOWN
的时候可以执行内部堆积任务(存储在workerQueue
内的任务)的条件,如果任何一个条件不成立,那么便不可以执行内部堆积任务,返回false。 -
▲[addWorker]如果运行的线程数量大于等于最大线程池容量( 2 29 − 1 2^{29}-1 229−1),或者要创建核心线程但是运行的线程数量大于等于核心线程的数量限制,或者创建非核心线程但是运行的线程数量大于等于最大线程数量的限制,返回false。
-
▲[addWorker]CAS,将运行线程的数量加一(并不是直接对ctl加一,而是对一个临时变量加一)
-
🔒▲[addWorker]接下来就是创建Worker了(你可以将Worker视为线程和任务的复合体),利用传入的firstTask(Runnable)创建一个Worker,那么Worker什么时候能够加入workers中成为线程池内的一个线程呢(我们将Worker粗略地视为一个线程)?
10.1 🔒▲[addWorker] 要么你的线程池正在运行
10.2 🔒▲[addWorker] 要么你的线程池处于
SHUTDOWN
状态,同时传入的fistTask是null,可以理解为:我厂子快关门了,正在赶紧把之前老客户的订单完成,完成后就关门,你可以催我,但是别给我订单了,不收了,task必须是null。 -
🔒▲[addWorker] 此时我们要判断创建的Worker内部的线程状态,如果线程还没加入workers便已经被调用了start方法,此时Worker内部的Thread是alive的,即Thread.alive是true,抛出异常。
-
🔒▲[addWorker] 上面都通过了,便可开启Worker内部的线程thread,执行thread.start方法,Worker本身实现了Runnable接口,实现了run方法,在new Worker的时候,内部的thread把Worker自身传入了Thread的构造方法中,当thread.start执行后,会调用Worker的run方法,接下来会调用runWorker(Worker)方法;
-
▲[runWorker] runWoker首先要获得需要执行的任务,首先看Worker内的firstTask是否为空,如果为空,则从任务队列中找;
-
🔒 ▲[runWorker] 接下来是设置线程的中断标志,这个标志是用来线程间通信的,本身没有啥作用。如果:
- 线程池处于
STOP\TIDYING\TERMINATED
状态并且当前线程没有设置中断标志,就算有也给先它清除掉然后再判断线程池状态是否为STOP\TIDYING\TERMINATED
状态 - 当前线程没有设置中断标志
如果满足以上的条件,给当前线程设置中断状态
- 线程池处于
-
🔒▲[runWorker] 执行获得的任务。