在Java里如何使用ExecutorService管理线程池_Java并发类库说明

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

ExecutorService 是什么,为什么不用 new Thread()?

它不是线程池本身,而是线程池的「控制接口」——ThreadPoolExecutorForkJoinPool 等具体实现都通过它暴露操作。直接 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.CallerRunsP

olicy() // 拒绝策略 );
  • corePoolSize 太小 → CPU 利用率低;太大 → 线程上下文切换开销剧增
  • 队列选 ArrayBlockingQueue(有界)比 LinkedBlockingQueue(默认无界)更可控
  • CallerRunsPolicy 在饱和时让调用线程自己执行任务,可自然降速,比 AbortPolicy(抛 RejectedExecutionException)更利于服务稳态

submit() 和 execute() 的关键区别在哪?

execute(Runnable) 只接受无返回值任务,不抛异常(异常会静默吞掉);submit() 返回 Future,支持获取结果、超时等待、主动取消,且未捕获异常会包装进 Future.get() 抛出:

Future future = 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() 就结束,没等任务真正结束:

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 等容器中,应在 @PreDestroyDisposableBean.destroy() 中执行关闭逻辑
实际最难的不是写对这几行代码,而是判断任务是否可中断、队列容量该设多大、以及拒绝后要不要重试或降级——这些都得结合业务吞吐模型和失败容忍度来定。