CachedThreadPool为何创建两个线程?调用shutdown()后为何仅创建一个?
问题:CachedThreadPool调用
shutdown()前后线程创建数量差异的原因 以下是我的Java代码:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; class ExceptionThread2 implements Runnable { @Override public void run() { Thread t = Thread.currentThread(); System.out.println("run() by " + t); System.out.println("eh = " + t.getUncaughtExceptionHandler()); throw new RuntimeException(); } } class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("caught " + e); } } class HandlerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { System.out.println(this + " creating new Thread "); Thread t = new Thread(r); System.out.println("created " + t); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); System.out.println("eh = " + t.getUncaughtExceptionHandler()); return t; } } public class CaptureUncaughtException { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory()); exec.execute(new ExceptionThread2()); //exec.shutdown(); } }
运行结果(未取消注释exec.shutdown()):
com.concurrent.example.HandlerThreadFactory@7f31245a creating new Thread created Thread[Thread-0,5,main] eh = com.concurrent.example.MyUncaughtExceptionHandler@6d6f6e28 run() by Thread[Thread-0,5,main] eh = com.concurrent.example.MyUncaughtExceptionHandler@6d6f6e28 com.concurrent.example.HandlerThreadFactory@7f31245a creating new Thread created Thread[Thread-1,5,main] eh = com.concurrent.example.MyUncaughtExceptionHandler@2870fdbb caught java.lang.RuntimeException
运行结果(取消注释exec.shutdown()):
com.concurrent.example.HandlerThreadFactory@7f31245a creating new Thread created Thread[Thread-0,5,main] eh = com.concurrent.example.MyUncaughtExceptionHandler@6d6f6e28 run() by Thread[Thread-0,5,main] eh = com.concurrent.example.MyUncaughtExceptionHandler@6d6f6e28 caught java.lang.RuntimeException
请问这是什么原因?
解答
这个差异的核心原因在于ThreadPoolExecutor(CachedThreadPool的底层实现)对异常终止线程的处理逻辑,以及shutdown()方法对线程池状态的改变。
1. 未调用shutdown()时创建第二个线程的原因
当线程池处于默认的RUNNING状态时:
- 你提交的
ExceptionThread2任务被分配给Thread-0执行,任务抛出RuntimeException导致Thread-0异常终止(不是正常完成任务)。 - ThreadPoolExecutor的
processWorkerExit方法会处理这个异常终止的Worker线程:- 首先将已死亡的Worker从线程池的管理集合中移除。
- 由于线程是突然终止的,方法最后会调用
addWorker(null, false)尝试添加一个新的Worker线程(这就是Thread-1被创建的原因)。 - 此时任务队列是空的,新创建的
Thread-1不会执行任何任务,会进入空闲等待状态,直到60秒后被自动回收(CachedThreadPool的空闲线程超时时间默认是60秒)。
2. 调用shutdown()后不再创建新线程的原因
当你调用exec.shutdown()后,线程池的状态会切换为SHUTDOWN:
- 同样,
Thread-0执行任务时抛出异常并终止。 - 在
processWorkerExit方法中,虽然线程是突然终止,但此时线程池处于SHUTDOWN状态:addWorker(null, false)方法会检查线程池状态和任务队列,在SHUTDOWN状态下,只有当任务队列非空时才会创建新的Worker线程,而此时队列是空的,所以不会创建新线程。
- 线程池会等待所有已提交的任务完成后,逐步关闭现有线程,不会再分配新的线程资源。
补充:CachedThreadPool的核心特性
Executors.newCachedThreadPool()的底层是ThreadPoolExecutor,配置了以下关键参数:
- 核心线程数为0,最大线程数为
Integer.MAX_VALUE - 使用
SynchronousQueue作为任务队列(任务直接传递给线程,不会在队列中等待) - 空闲线程超过60秒会被自动回收
这种配置让它能根据任务量动态创建线程,但当线程因异常终止且线程池处于RUNNING状态时,会自动补充新的Worker线程以维持线程池的可用性。
内容的提问来源于stack exchange,提问作者bfq
相关产品推荐
相关产品推荐

