1. 线程复用
我们知道Thread.start
执行之后,线程就能再次执行了,那ThreadPoolExecutor
是如何做到线程复用的呢?
原理很简单,在实际执行的线程外部套一个Thread
,外层Thread
的run
方法while
循环执行实际执行线程的run
方法,实现线程的复用并且执行之后不销毁。下面是伪代码:
// 任务等待队列
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue();
new Thread(() -> {
for (;;){
Runnable runnable = taskQueue.poll();//队列里拿
runnable.run();//同步执行
}
}).start();// 异步while执行
下面是ThreadPoolExecutor
的重点代码:
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 ((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);
}
}
2. 线程销毁
我们知道,在创建线程池的时候有超时参数keepAliveTime
,那么线程池是如何实现精确的超时销毁呢?
这个是结合BlockingQueue
的阻塞超时来实现的,下面是源码:
/**
* ...
* @return task, or null if the worker must exit, in which case workerCount is decremented
* 翻译: 返回task,如果worker必须退出,则返回null,在这种情况下workerCount递减
*/
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;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // 重点在这,如果超时没有获取到任务,则返回null,销毁线程。
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
3. 为什么用BlockingQueue
- 获取等待任务的时候,直接用阻塞代替通知轮询,提高性能,减少代码复杂度。
- 复用阻塞超时获取等待任务实现线程超时销毁,设计精巧。
- 本身就是支持并发操作的,不用额外维护线程安全。
参考
- 一. 线程池简介