07_03_中断响应
中断响应
在取消任务和中断线程中,中断都起到了很大作用:
- 取消任务,最合理的方式就是使用中断
- 中断线程,线程收到中断后作何处理
但是有个问题:
- 任务和线程都能接收中断请求,那怎么区分?
也就是,中断响应由谁来负责?
一、区分任务取消和线程中断
- 任务取消和线程中断,是 2 种不同的行为
- 任务取消的目的是结束任务执行;线程中断的目的是向线程发送中断信号
- 任务取消的对象是任务;线程中断的对象是线程
- 中断事件,是从下往上冒泡的,而任务运行在线程内,所以是任务先收到中断,才轮到线程
二、避免任务屏蔽中断
由于是任务先收到中断,如果任务把中断屏蔽了,那后面线程就收不到中断信息了。
所以,不管在何种情况下:
- 任务收到中断后,无论任务是否响应中断,都不应该清除中断信息
- 只有实现了中断策略的代码才可以屏蔽中断请求
也就是说,不要在任务中随便屏蔽中断信号。
错误示例:
1 | public void run() { |
正确做法:
1 | public void run() { |
一般来说,如果没有实现中断策略,在收到中断后有2种处理策略:
- 传递异常:将中断异常抛出,交由调用者处理。比如代码库都是这样做的
- 恢复中断状态:不想或没办法传递异常时,可以再次调用
interrupt
方法来恢复中断状态
任务只要保证中断信息能留下来即可,至于什么时候恢复,可以自由选择。
比如,可以等所有任务执行完成后,再恢复中断信息:
1 | class DelayExitThread extends Thread { |
不同的任务,可能中断处理方式不同,但最终应该都能够保留中断状态才对。
除非确认中断信号已经没用了,否则任务万不可屏蔽中断请求。
三、线程只能由所有者中断
由于每个线程都有各自的中断策略,所以不要随便中断一个未知的线程。
- 线程只能由其所有者中断,或者知晓中断对线程的含义才可以中断,否则不应该中断该线程
因为不清楚中断策略,就不知道中断线程后会发生什么,会导致什么结果。
比如说,线程在收到中断后立即退出:
1 | class ExitThread extends Thread { |
这种情况下,中断后剩余未做的任务就不会再执行了,而这未必是任务提交人所要的结果。
所以,中断前必须先了解线程的中断策略,否则胡乱使用中断可能引发很多不可预料的结果。
四、单独取消任务
既然要把任务取消和线程中断区分开,那么应该怎么区分呢?
虽然都是用中断,但是可以将它们俩的调用方式区分开来。
- 任务取消:封装一套任务取消的方式,比如
Future.cancel()
- 线程中断:依旧使用原有的接口
Thread.interrupt()
通过使用不同的方式,就能大致区分它们(注意不是绝对的)。
可以单独封装任务的取消,比如这样做:
1 | class CancelableTask implements Runnable { |
最后这里为什么要清除中断信息?其实目的为了区分任务取消和线程中断。
但这种方式并不完美,本意只是为了清除任务取消引起的中断,但是实际上有可能清除了别的中断:
1 | task.cancel(); // 1 |
如果同时执行任务取消和线程中断,那么取消(1)就有可能把中断(2)的中断信息给清除了。
实际上,Java 的类库中已经封装好了任务的取消操作,就是 Future.cancel()
。
Future.cancel()
原理和上面代码类似,不过在最后没有清除中断信息,因为有可能清除掉别的中断。
不过还是建议,取消任务时尽量使用 Future.cancel()
,而不要直接用 Thread.interrupt()
。
总结
区分任务取消和线程中断
- 任务取消和线程中断,是 2 种不同的行为
- 任务取消的目的是结束任务执行;线程中断的目的是向线程发送中断信号
- 任务取消的对象是任务;线程中断的对象是线程
- 中断事件,是从下往上冒泡的,而任务运行在线程内,所以是任务先收到中断,才轮到线程
避免任务屏蔽中断
- 任务收到中断后,无论任务是否响应中断,都不应该清除中断信息
- 只有实现了中断策略的代码才可以屏蔽中断请求
一般来说,如果没有实现中断策略,在收到中断后有2种处理策略:
- 传递异常:将中断异常抛出,交由调用者处理。比如代码库都是这样做的
- 恢复中断状态:不想或没办法传递异常时,可以再次调用
interrupt
方法来恢复中断状态
线程只能由其所有者中断,或者知晓中断对线程的含义才可以中断,否则不应该中断该线程
单独取消任务
- 任务取消:封装一套任务取消的方式,比如
Future.cancel()
- 线程中断:依旧使用原有的接口
Thread.interrupt()
- 任务取消:封装一套任务取消的方式,比如
线程中断实际上依赖于任务的中断处理,如果任务将中断屏蔽了,那么线程将不会收到中断