线程池的优雅关闭实践shutdown()、shutdownNow()和awaitTermination(long timeOut, TimeUnit unit)

平时开发中,大家更多的关注的是线程池的创建、任务的提交和执行。往往会忽略线程池的关闭,甚至忘记调用shutdown()方法,导致内存溢出。大多知道需要调用shutdown()关闭线程池,也少研究其真正的关闭过程。
首先看源码中的一句注释:

A pool that is no longer referenced in a program and has no remaining threads will be shutdown automatically.
如果程序中不再持有线程池的引用,并且线程池中没有线程时,线程池将会自动关闭。

线程池自动关闭的两个条件:1、线程池的引用不可达;2、线程池中没有线程;
这里对于条件2解释一下,线程池中没有线程是指线程池中的所有线程都已运行完自动消亡。然而我们常用的FixedThreadPool的核心线程没有超时策略,所以并不会自动关闭。
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()是无效的;而内部该怎么跑还是怎么跑,跑完再停。
shutdown()和awaitTermination()的区别
shutdown()后,不能再提交新的任务进去;但是awaitTermination()后,可以继续提交。awaitTermination()是阻塞的,返回结果是线程池是否已停止(true/false);shutdown()不阻塞。
总结

  • 优雅的关闭,用shutdown()
  • 想立马关闭,并得到未执行任务列表,用shutdownNow()
  • 优雅的关闭,并允许关闭声明后新任务能提交,用awaitTermination()

关闭功能 【从强到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()。

posted @ 2021-07-04 15:53  郭慕荣  阅读(668)  评论(0编辑  收藏  举报