关闭线程池不是简单的shutdown(),而是一套组合拳:拒绝新任务→等待完成→强制关闭→清理资源!
一、两种关闭方式对比
shutdown() - 温柔派
executor.shutdown();
行为:
- ✅ 不再接受新任务
- ✅ 执行已提交的任务(队列中的继续执行)
- ⏳ 等待所有任务完成
生活类比: 餐厅打烊🍽️
- 不接新客人
- 服务完已经点单的客人
- 等所有客人吃完
shutdownNow() - 强硬派
List<Runnable> notExecuted = executor.shutdownNow();
行为:
- ✅ 不再接受新任务
- ⚠️ 尝试中断正在执行的任务
- ❌ 队列中的任务不执行,直接返回
生活类比: 餐厅紧急疏散🚨
- 不接新客人
- 正在吃的客人也请离开
- 已点单但未做的菜不做了
对比表
| 特性 | shutdown | shutdownNow |
|---|---|---|
| 新任务 | ❌ 拒绝 | ❌ 拒绝 |
| 队列中的任务 | ✅ 执行 | ❌ 不执行,返回列表 |
| 正在执行的任务 | ✅ 继续 | ⚠️ 尝试中断 |
| 返回值 | 无 | 未执行的任务列表 |
二、标准关闭流程(推荐)⭐
public void shutdownGracefully(ExecutorService executor) {
log.info("开始关闭线程池...");
// 1. 停止接受新任务
executor.shutdown();
try {
// 2. 等待60秒
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
log.warn("等待超时,强制关闭");
// 3. 强制关闭
List<Runnable> droppedTasks = executor.shutdownNow();
log.warn("丢弃任务数: {}", droppedTasks.size());
// 4. 再等待30秒
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
log.error("线程池无法关闭");
}
}
} catch (InterruptedException e) {
log.error("关闭被中断");
executor.shutdownNow();
Thread.currentThread().interrupt();
}
log.info("线程池已关闭");
}
三、完整示例
public class ThreadPoolShutdownDemo {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 提交10个任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.execute(() -> {
try {
System.out.println("任务" + taskId + "开始执行");
Thread.sleep(2000);
System.out.println("任务" + taskId + "执行完成");
} catch (InterruptedException e) {
System.out.println("任务" + taskId + "被中断");
}
});
}
Thread.sleep(1000);
// 优雅关闭
shutdownGracefully(executor);
}
private static void shutdownGracefully(ExecutorService executor) {
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
List<Runnable> dropped = executor.shutdownNow();
System.out.println("强制关闭,丢弃" + dropped.size() + "个任务");
executor.awaitTermination(2, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("线程池已关闭");
}
}
四、Spring Boot优雅停机
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(1000);
executor.setThreadNamePrefix("async-");
// 等待任务完成后再关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
// 最多等待60秒
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
五、面试高频问答💯
Q: shutdown和shutdownNow的区别?
A:
- shutdown:优雅关闭,执行完队列中的任务
- shutdownNow:强制关闭,尝试中断正在执行的任务
Q: shutdownNow能立即停止任务吗?
A: 不能! 只是发送中断信号,任务需要响应中断才能停止。
Q: 如何判断线程池是否关闭?
A:
executor.isShutdown(); // 是否调用了shutdown
executor.isTerminated(); // 是否所有任务都完成
Q: 线程池关闭后还能提交任务吗?
A: 不能,会抛出RejectedExecutionException。
下一篇→ ThreadLocalRandom为啥比Random快?🎲