你的线程池关闭了吗?

你的线程池关闭了吗?

用ExectorService去创建线程池时,如果不手动shutdown,一般情况下 tomcat关闭时是不会关闭它的,这样会造成tomcat关闭失败或者僵尸进程。

解决的办法是:

1 通过Listener绑定销毁方法,在destroy时关闭线程池。

2 将创建线程池的线程设置为daemon setDaemon(true)

3 tomcat内部线程池创建线程的思路是先创建使用,然后根据超时和使用情况interruppt掉不用的线程,这样不会导致线程资源的浪费。而不用关闭线程池。


对于全局的,考虑如何优雅的关闭.


首先看下ThreadPoolExecutor源码注释:


<code> * 
A pool that is no longer referenced in a program AND
* has no remaining threads will be {@code shutdown} automatically. If
* you would like to ensure that unreferenced pools are reclaimed even
* if users forget to call {@link #shutdown}, then you must arrange
* that unused threads eventually die, by setting appropriate
* keep-alive times, using a lower bound of zero core threads and/or

* setting {@link #allowCoreThreadTimeOut(boolean)}.

*
*
/<code>

如果程序中不再持有线程池的引用,并且线程池中没有线程时,线程池将会自动关闭。

线程池自动关闭的两个条件:

  • 线程池的引用不可达;
  • 线程池中没有线程。

这里对于条件2解释一下,线程池中没有线程是指线程池中的所有线程都已运行完自动消亡。然而如果我们ThreadPool的核心线程没有超时策略,线程池并不会自动关闭。

还有,虽然ThreadPoolExecutor提供了shutdownNow()方法,在调用该方法后会尝试中断所有线程,但是该中断并不能保证线程一定会就此终止,因此,需要开发者实现线程中断的策略。当然还可以通过设置线程池为守护线程的方式。

另外,想要正确的关闭线程池,并不是简单的调用shutdown方法那么简单,要考虑到应用场景的需求,如何拒绝新来的请求任务?如何处理等待队列中的任务?如何处理正在执行的任务?想好这几个问题,在确定如何优雅而正确的关闭线程池。有关这方面guava提供了好的方法

  • https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/MoreExecutors.java
  • https://github.com/springside/springside4/blob/master/modules/utils/src/main/java/org/springside/modules/utils/concurrent/threadpool/ThreadPoolUtil.java

最后,我认为我们需要更多思考如何制定优雅的停机策略以及如何优雅的关闭线程池。

我们知道在停机或重启时需要做一些“善后”的程序处理,一般会注册shutdownHook,尽管 JVM 关闭时会帮我们回收一定的资源,但一些服务如果大量使用异步回调,定时任务,处理不当很有可能会导致业务出现问题,在这其中,线程池如何关闭是一个比较典型的问题。在一般情况下,这不会有什么大问题,因为 JVM 关闭,会释放线程池,但弊端在于线程池中提交的任务以及阻塞队列中未执行的任务变得极其不可控,接收到停机指令后是立刻退出?还是等待任务执行完成?抑或是等待一定时间任务还没执行完成则关闭?所以,我们需要想办法在应用关闭时(JVM 关闭,容器停止运行),关闭线程池。


分享到:


相關文章: