【线程池】threadPoolExecutor 中的 shutdown() 、 shutdownNow() 、 awaitTermination() 的用法和区别

本文详细解析了ThreadPoolExecutor中的shutdown(), shutdownNow()和awaitTermination()方法的使用及区别。shutdown()优雅地停止线程池,不再接受新任务但完成现有任务;shutdownNow()尝试立即停止所有任务并返回未执行任务列表;awaitTermination()阻塞当前线程直到所有任务完成或超时。

使用到ThreadPoolExecutor时,其中包含 关闭方法(shutdown()shutdownNow()awaitTermination())下面一起来看看这三个方法:

shutdown()

将线程池状态置为SHUTDOWN,并不会立即停止:

  • 停止接收外部submit的任务
  • 内部正在跑的任务和队列里等待的任务,会执行完
  • 等到第二步完成后,才真正停止

shutdownNow()

将线程池状态置为STOP企图立即停止,事实上不一定:

  • 跟shutdown()一样,先停止接收外部提交的任务
  • 忽略队列里等待的任务
  • 尝试将正在跑的任务interrupt中断
  • 返回未执行的任务列表

它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。

但是大多数时候是能立即退出的

awaitTermination(long timeOut, TimeUnit unit)

当前线程阻塞,直到

  • 等所有已提交的任务(包括正在跑的和队列中等待的)执行完
  • 或者等超时时间到
  • 或者线程被中断,抛出InterruptedException

然后返回true(shutdown请求后所有任务执行完毕)或false(已超时)

实验发现,shuntdown()和awaitTermination()效果差不多,方法执行之后,都要等到提交的任务全部执行完才停。

 

shutdown()和shutdownNow()的区别

从字面意思就能理解,shutdownNow()能立即停止线程池,正在跑的和正在等待的任务都停下了。这样做立即生效,但是风险也比较大;
shutdown()只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。

 

Between client threads and thread pool there is a queue of tasks. When your application shuts down, you must take care of two things: what is happening with queued tasks and how already running tasks are behaving (more on that later). Surprisingly many developers are not shutting down thread pool properly or consciously. There are two techniques: either let all queued tasks to execute (shutdown()) or drop them (shutdownNow()) - it totally depends on your use case.

shutdown()和awaitTermination()的区别

shutdown()后,不能再提交新的任务进去;但是awaitTermination()后,可以继续提交。
awaitTermination()是阻塞的,返回结果是线程池是否已停止(true/false);shutdown()不阻塞。

 

总结

  • 优雅的关闭,用shutdown()
  • 想立马关闭,并得到未执行任务列表,用shutdownNow()
  • 优雅的关闭,并允许关闭声明后新任务能提交,用awaitTermination()
关闭功能 【从强到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()
在使用 `ThreadPoolExecutor` 线程池时,调用 `shutdown()` 方法后,线程池会进入 **SHUTDOWN** 状态,此时线程池不会接受新的任务,但会继续执行队列中已提交的任务。一旦任务队列也被清空,线程池将进入 **TERMINATED** 状态。然而,Java 的线程池设计中并没有提供直接重新启动线程池的 API,因此需要通过特定方式来实现重新启动的需求。 ### 重新启动线程池的方法 1. **创建新的线程池实例** 最直接的方式是放弃旧的线程池实例,并创建一个新的 `ThreadPoolExecutor` 实例。这种方式适用于任务生命周期明确、线程池可以被丢弃的场景。例如: ```java ExecutorService newExecutor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy() ); ``` 2. **自定义线程池管理类** 可以封装一个线程池管理类,该类负责创建、销毁重新初始化线程池。通过此类,可以在需要时关闭旧线程池并启动新线程池,同时保持接口一致性。例如: ```java public class CustomThreadPoolManager { private ExecutorService executor; public void initThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime) { this.executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() ); } public void restartThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime) { if (executor != null && !executor.isTerminated()) { executor.shutdown(); try { if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { executor.shutdownNow(); } } catch (InterruptedException e) { executor.shutdownNow(); } } initThreadPool(corePoolSize, maxPoolSize, keepAliveTime); } public ExecutorService getExecutor() { return executor; } } ``` 3. **通过任务队列控制线程池状态** 可以利用任务队列中的任务调度逻辑,动态地将新任务提交到新的线程池中。这种方式适用于需要保留任务队列内容的场景,可以通过任务队列转移实现无缝切换。 4. **使用线程池工厂方法重新创建** 如果使用了 `Executors` 工厂方法创建线程池,也可以在调用 `shutdown()` 后再次调用工厂方法重新生成线程池实例。例如: ```java ExecutorService executor = Executors.newFixedThreadPool(10); executor.shutdown(); executor = Executors.newFixedThreadPool(10); // 重新创建 ``` ### 注意事项 - **线程资源释放**:在重新创建线程池之前,必须确保旧线程池中的任务已完全执行完毕或被强制中断,否则可能会导致资源泄漏。 - **任务队列管理**:如果任务队列中仍有未执行的任务,需手动迁移至新线程池中,否则任务将丢失。 - **线程安全问题**:在并发环境中重新创建线程池时,应使用同步机制确保线程安全。 ### 总结 `ThreadPoolExecutor` 并不支持直接重启线程池,因此需要通过重新创建线程池实例或封装管理类来实现类似功能。上述方法可根据实际需求选择使用,确保线程池状态可控且任务不丢失。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值