异步调用(二)线程池的优雅关闭
一、线程中断
在介绍线程池关闭之前,先介绍下Thread的interrupt。
在程序中,我们是不能随便中断一个线程的,因为这是极其不安全的操作,我们无法知道这个线程正运行在什么状态,它可能持有某把锁,强行中断可能导致锁不能释放的问题;或者线程可能在操作数据库,强行中断导致数据不一致混乱的问题。正因此,Java里将 Thread
的 stop()
方法设置为过时,以禁止大家使用。
那么一个线程什么时候可以退出呢?当然只有线程自己才能知道。
所以我们这里要说的 Thread
的 interrupt()
方法,本质不是用来中断一个线程。是将线程设置一个中断状态。当我们调用线程的interrupt方法,它有两个作用:
如果此线程处于阻塞状态(比如调用了
wait()
方法,io等待),则会立马退出阻塞,并抛出InterruptedException
异常,线程就可以通过捕获InterruptedException
异常来做一定的处理,然后让线程退出。如果此线程正处于运行之中,则线程不受任何影响,继续运行,仅仅是线程的中断标记被设置为true。所以线程要在适当的位置通过调用
isInterrupted()
方法来查看自己是否被中断,并做退出操作。
注:
如果线程的interrupt方法先被调用,然后线程调用阻塞方法进入阻塞状态,
InterruptedException
异常依旧会抛出。如果线程捕获
InterruptedException
异常后,继续调用阻塞方法,将不再触发异常。
二、线程池的关闭方法
线程池提供了两个关闭方法:
1. shutdown()
停止接收新任务,原来的任务继续执行:
- 停止接收新的submit的任务;
- 已经提交的任务(包括正在跑的和队列中等待的),会继续执行完成;
- 等到第2步完成后,才真正停止;
- 返回未执行的任务列表;
2. shutdownNow()
停止接收新任务,原来的任务停止执行:
- 同 shutdown() 方法 ,先停止接收新submit的任务;
- 忽略队列里等待的任务;
- 尝试将正在执行的任务interrupt中断;
- 返回未执行的任务列表;
说明:它试图终止线程的方法是通过调用
Thread.interrupt()
方法来实现的,这种方法的作用有限,如果线程中没有sleep
、wait
、Condition
、定时锁等应用,interrupt()
方法是无法中断当前的线程的。所以,shutdownNow()
并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。
3. awaitTermination(long timeOut, TimeUnit unit)
当前线程阻塞,timeout 和 TimeUnit 两个参数,用于设定超时的时间及单位,当前线程阻塞,直到:
- 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
- 或者 等超时时间到了(
timeout
和TimeUnit
设定的时间); - 或者 线程被中断,抛出
InterruptedException
异常
然后会监测 ExecutorService 是否已经关闭,返回 true
(shutdown请求后所有任务执行完毕)或 false
(已超时)
4. 三种方法的区别
shutdown() 与 shutdownNow()
shutdown()
只是关闭了提交通道,用submit()
是无效的;而内部该怎么跑还是怎么跑,跑完再停。shutdownNow()
能立即停止线程池,正在跑的和正在等待的任务都停下了。
shutdown() 与 awaitTermination()
shutdown()
后,不能再提交新的任务进去;但是awaitTermination()
后,可以继续提交。awaitTermination()
是阻塞的,返回结果是线程池是否已停止(true/false);shutdown()
不阻塞。
5. 总结
- 优雅的关闭,用
shutdown()
- 立马关闭,并得到未执行任务列表,用
shutdownNow()
- 优雅的关闭,并允许关闭声明后新任务能提交,用
awaitTermination()
- 关闭功能 【从强到弱】 依次是:
shuntdownNow()
>shutdown()
>awaitTermination()
三、如何实现线程池的优雅关闭
addShutdownHook 原理
RunTime.getRunTime().addShutdownHook()
的作用就是在 JVM
销毁前执行的最后一个线程,通过addShutdownHook
添加钩子,当系统执行完这些钩子后,JVM
才会关闭,因此我们可以在这个线程中把我们前面使用 ExecutorService
创建的线程池优雅地关闭掉。
addShutdownHook 优雅关闭线程池
在以下示例中,手动关闭 JVM
后,会触发线程池的优雅关闭:
1 |
|