Java 中的 ThreadPoolExecutor 是 JUC 包中十分重要的成员,主要提供了一个线程池管理的工作。关于它的用法,很多博客都有提及,包括 corePoolSize, 阻塞队列 blockQueue, 最大池大小 maxPoolSize, 拒绝策略 RejectedExecutionHandler ,和存活时间 keepAliveTime, 都已经有很多文章。我们都知道 keepAliveTime 指的是当当前线程池中线程大于 corePoolSize 的时候,如果超过 keepAliveTime 还没有新的任务,则释放大于 corePoolSize 部分的线程。但是它是怎么实现的,本文我就来给大家剖析一下。
在开始看源码之前,我们先思考一下,如果要做到超时释放,应该怎么做。一种简单的方式就是启动一个线程,这个线程不断的轮询线程池当前线程的状态,发现有空闲的线程则调用中断方法尝试中断线程。
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);
execute 方法很好理解,就是判断当前线程池中活跃线程是否小于 corePoolSize, 如果超过,则进入阻塞队列,还不行就创建新 worker,如果创建失败了,那么就进入拒绝策略。我们可以看到关键的执行方法是 addWorker 方法,然后我们看看 addWorker 做了什么
addWorker 分为两个部分,我们先来看第一个部分
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))
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 操作的 compareAndIncrementWorkerCount 的方法,更新线程池当前运行线程数量,除非发现当前线程数已经超过 maxPoolSize, 或者线程池当前不处于运行状态。当更新当前运行线程数量成功后,进入第二个阶段
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());
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
类的实例,这个 Worker 内有一个 thread 实例,这里会再一次检查状态,并且成功后,将当前 worker 实例加入 workers 工作队列集合中。然后让 worker 中的 thread start。这里我们还看不到线程是如何超时结束的,我们继续跟进,看看 Worker 中线程 start 做了什么。
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
Worker 方法自身继承了 Runnable 方法,内部持有的线程直接以 Worker 实例自身来初始化线程。所以 thread start 调用了
worker 的 run 方法
public void run() {
runWorker(this);
}
好了,我们直接来看 runWorker 方法
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 ((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);
}
这个 runWorker
可以说才是真正的核心方法,首先这个方法会调用 task.run() 方法,我们可以任务线程的任务就是在这里被执行的,不过有趣的是 task 可以不是 Worker 创建的时候携带的 task,它可以是从 getTask 方法中获取到的任务。看到这里我们可以得到一个结论。
创建出来的线程,会一直执行,直到无法通过 getTask 方法获取到新的任务为止。这个时候线程会退出
感觉我们已经快要到目的地了,现在我们来看看 getTask 方法
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 只有到了必要的时候,才会检查等待队列是否为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
因为线程池要处理并发状况下的问题,所以 getTask 又无限确认了当前线程池的状态,我们看最后一段关键代码
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
代码看到现在,我们终于看到期盼已久的 keepAliveTime, 很失望和我们最开始的猜想并不一样,keepAliveTime 并不是通过一个线程轮询检查实现的,而是
通过阻塞队列 workQueue 的 poll 方法,当超过 keepAliveTime 的时候后还无法获取新的任务,则返回 null,
最后在 runWorker 方法中结束线程整个生命
至此,我们已经看到了 keepAliveTime 的实现方式。
completedAbruptly 用于指明线程 run 方法是否是正常结束,如果非正常结束,则进入 processWorkerExit 方法后,会重新将线程通过 addWorker 方法提交,只不过这次它不会再携带默认方法了而已。
我们知道 Thread 线程方法只能在初始化的时候传入 Runnable 实例,如果我们想切换 Runnable 是没有办法的。但是看过 ThreadPoolExecuter 的源码后,我们可以使用如下的方式,让 Thread 执行完成自身的 runnable 后,切换下一个 runnable.
BlockingQueue queue = new ArrayBlockingQueue(100);
queue.add( () -> System.out.println("task1"));
queue.add( () -> System.out.println("task2"));
new Thread(() -> {
try {
queue.take().run();
} catch (InterruptedException e) {
e.printStackTrace();
}
});