Java 线程池取消的方式
- 线程池取消分为取消线程池中单个任务和关闭线程池本身,过程分为停止接收新任务、终止正在执行的任务以及清理未执行的任务
Future#cancel 取消单个任务
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("请求线程取消");
boolean cancelled = future.cancel(true);
System.out.println("任务是否取消成功:" + cancelled);
executor.shutdown();
}
shutdown 平滑关闭
- 停止接收新任务,会等待已提交的任务(包括正在执行的和队列中等待的)执行完成后再关闭
- 可以确保已提交的任务都是正常结束的
executor.shutdown();
shutdownNow 强制关闭
- 内部会调用 Thread#interrupt 方法,能否通过 interrupt 有效取消任务,取决于任务本身响应中断的逻辑
- 停止接收新任务,尝试立即关闭线程池,返回尚未执行的任务列表,并尝试中断正在执行的任务
- 后续可以对尚未执行的任务进行处理,比如记录日志或重新提交
List<Runnable> cancelledTasks = executor.shutdownNow();
System.out.println("被取消的任务数: " + cancelledTasks.size());
线程池关闭最佳实践
private void executorShutdown() {
if (executor.isShutdown()) {
return;
}
executor.shutdown();
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
System.err.println("线程池未完全终止");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
总结
- 任务响应中断:使用 Future.cancel(true) 或 shutdownNow 时,任务自身需要通过 Thread.currentThread().interrupted() 或InterruptedException 检测中断状态并响应中断
- 优先使用 shutdown 仅在必要时使用 shutdownNow