ExecutorService 是线程池的控制接口,非线程池本身;不用 new Thread() 是因后者无法复用、易OOM、难监控和关闭,而 ExecutorService 提供统一提交、拒绝策略、生命周期管理等能力。

ExecutorService 是什么,为什么不用 new Thread()?
它不是线程池本身,而是线程池的「控制接口」——ThreadPoolExecutor、ForkJoinPool 等具体实现都通过它暴露操作。直接 new Thread() 会快速耗尽系统资源,且无法复用、监控或优雅关闭;而 ExecutorService 提供统一的提交任务、批量关闭、拒绝策略、生命周期管理能力。
如何创建和选择合适的线程池类型?
别无脑用 Executors.newFixedThreadPool(4) —— 它底层用的是无界 LinkedBlockingQueue,任务积压时可能 OOM。生产环境推荐显式构造 ThreadPoolExecutor:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize
4, // maxPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue(100), // 有界队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
-
corePoolSize太小 → CPU 利用率低;太大 → 线程上下文切换开销剧增 - 队列选
ArrayBlockingQueue(有界)比LinkedBlockingQueue(默认无界)更可控 -
CallerRunsPolicy在饱和时让调用线程自己执行任务,可自然降速,比AbortPolicy(抛RejectedExecutionException)更利于服务稳态
submit() 和 execute() 的关键区别在哪?
execute(Runnable) 只接受无返回值任务,不抛异常(异常会静默吞掉);submit() 返回 Future,支持获取结果、超时等待、主动取消,且未捕获异常会包装进 Future.get() 抛出:
Futurefuture = executor.submit(() -> { Thread.sleep(100); return "done"; }); // 必须调用 get() 才能触发异常传播 String result = future.get(2, TimeUnit.SECONDS); // 可能抛 ExecutionException / TimeoutException
- 若任务逻辑可能抛受检异常,必须用
submit()+try-catch get() -
execute()适合纯异步日志、埋点等“发了就忘”的场景 - 永远不要忽略
Future的返回值——不get()或cancel(),异常会丢失,且任务状态无法感知
如何安全关闭线程池?
调用 shutdown() 后,线程池不再接收新任务,但会等已有任务完成;shutdownNow() 尝试中断所有正在运行的线程(依赖任务自身响应 Thread.interrupted())。常见错误是只调 shutdown() 就结束,没等任务真正结束:
立即学习“Java免费学习笔记(深入)”;
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 超时后强制终止
if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
-
awaitTermination()必须配合shutdown()使用,单独调无效 - 中断不是“杀死”,只是设中断标志;任务里需定期检查
Thread.currentThread().isInterrupted()并退出循环 - Spring 等容器中,应在
@PreDestroy或DisposableBean.destroy()中执行关闭逻辑










