网上介绍ThreadPoolExecutor的文章有很多,而且都介绍的特别详细,可能很多人(主要是我),会花很多时间去理解里面的细节,我想从另一个角度,来看ThreadPoolExecutor的实现。让我们来看看它的整体思路及骨骼框架代码是什么样的呢?
骨骼代码
任何一个优秀的代码,想要在整体上把握它,拎出了它的骨骼代码至关重要,在通读了ThreadPoolExecutor的代码后,其实发现他的骨骼代码很简单。
/**
* @Date 2022/5/16 22:56
**/
public class ThreadPoolExecutorFrameWork {
private final HashSet<Worker> workers = new HashSet<Worker>();
// execute(...) 方法
public void execute(Runnable command) {
addWorker(command);
}
void addWorker(Runnable firstTask) {
Worker worker = new Worker(firstTask);
final Thread thread = worker.thd;
workers.add(worker);
thread.start(); //新线程启动
}
void runWorker(Worker worker) {
Runnable task = worker.firstTask;
try {
if(task != null) {
task.run();
}
} finally {
processWorkerExit(worker);
}
}
private void processWorkerExit(Worker w) {
workers.remove(w); // 线程回收
}
// shutdown() 方法
public void shutdown() {
interruptIdleWorkers();
}
private void interruptIdleWorkers() {
for (Worker w : workers) {
Thread t = w.thd;
// if idle, 判断是否空闲 w.tryLock
t.interrupt();
System.out.println("shutdown:: "+t.getName());
}
}
final class Worker implements Runnable {
Runnable firstTask;
final Thread thd;
public Worker(Runnable firstTask) {
this.firstTask = firstTask;
// thd 必须用以下形式,不能用new Thread()这种。
this.thd = new Thread(null, this, "thread-1", 0);
}
public void run() {
runWorker(this);
}
}
// 测试数据
Runnable runnable1 = () -> System.out.println("123");
// 测试用例
void test() {
execute(runnable1);
execute(null);
// 需要加锁,控制顺序
shutdown();
new Thread(() -> System.out.println("test.....")).start();
}
public static void main(String[] args) {
ThreadPoolExecutorFrameWork test3 = new ThreadPoolExecutorFrameWork();
test3.test();
}
}
以上骨骼代码中,展示了我们常用的ThreadPoolExecutor中的execute(…) 、 shutdown()等方法是如何工作的,以及线程回收的做法。有了骨骼代码,就很容易理解线程池究竟是怎么实现的了。
当然,我这里缺少了很多细节的处理,没有核心线程,没有并发控制,没有状态转换及判断。有了骨骼,只要在需要的地方添血肉就好了。
线程回收
线程池中线程的销毁依赖JVM自动的回收,移除线程池对该线程的引用,使其可以被JVM正常地回收。
ThreadPoolExecutor的线程回收主要靠以下方法。
try {
while (task != null || (task = getTask()) != null) {
//执行任务
}
} finally {
processWorkerExit(w, completedAbruptly);//获取不到任务时,主动回收自己
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
// 移除线程池对该线程的引用,使其可以被JVM正常地回收
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
//若允许销毁空闲的核心线程,则允许剩余的线程数为0
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//发现阻塞队列有任务,则允许剩余的线程数为1
if (min == 0 && ! workQueue.isEmpty())
min = 1;
//工作线程数必须大于等于最小线程数,才允许销毁自己。
if (workerCountOf(c) >= min)
return; // replacement not needed
}
//继续while循环去处理任务(该工作线程未被销毁)
addWorker(null, false);
}
}
线程池的状态
关于线程池的几个状态,我觉得看源码的注释更好。
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.
详细解析,可以参考这里。
Java线程池实现原理及其在美团业务中的实践
ThreadPoolExecutor 优雅关闭线程池的原理.md