文章目录
- 一、简介
- (1)问题
一个在行为良好的软件与勉强运行的软件之间的最主要区别就是:
行为良好的软件能很完善地处理失败、关闭和取消
一、简介
Java
没有提供任何机制来安全地终结线程。
Thread.stop** 和
suspend`等方法提供了终结,但由于存在一些严重的缺陷,因此应该避免使用
中断Interruption
:这是一中协作机制,能够使一个线程终止另一个线程的当前工作。
(1)问题
1. 为什么需要线程中断?
用于处理失败、关闭和取消等过程。
- 在
Java
中没有一种安全的抢占式方法来停止线程,因此也就没有安全的抢占方式来停止任务。
即,采用协作式的机制(interruption),使请求取消的任务和代码都遵循一种协商好的协议。
通过推迟中断请求的处理,开发人员能指定更灵活的中断策略,从而使应用程序在响应性和健壮性之间实现合理的平衡。
2. 什么是线程中断?
中断:它并不会真正地中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己。
线程在调用
Object class
的wait()
join()
sleep()
方法时被阻塞,那么interrupt
会生效,该线程的中断状态将被清除,抛出InterruptedException
异常如果目标线程是被 I/O 或者 NIO中的
Channel
所阻塞,同样,I/O操作会被中断或者返回特殊异常值。达到终止线程的目的。如果以上条件都不满足,则会设置此线程的中断状态
每个线程都有一个boolean
类型的中断状态。
当中断线程时,这个线程的中断状态将被设置为 true。
3. 有什么中断策略?
当发现中断请求时,应该做哪些工作(如果需要的话),哪些工作单元对于中断来说是原子操作,以及以多快的速度来响应中断。
最合理的中断策略:某种形式的线程级(Thread-Level)取消操作或服务级(Service-Level)
取消操作:尽快退出,在必要时进行清理,通知某个所有者该线程已退出。
区分 任务 与 线程:
- 任务:不会在其自己的拥有的线程中执行,而是在某个服务(例如线程池)拥有的线程中执行。
对于非线程所有者的代码来说(例如,对于线程池而言,任何在线程池实现以外的代码),应该小心地保存中断状态,这样拥有线程的代码才能对中断作出响应,即使 “非所有者” 代码也可以作出响应。
比如:
当你为一户人家打扫房屋时,即使主人不在,也不应该把在这段时间内收到的邮件扔掉,而应该把邮件收起来,等主人回来以后再交给他们处理
4. 如何响应中断?
当调用可中断的阻塞函数时,例如Thread.sleep
或BlockingQueue.put
等
有两种实用策略可用于处理 InterruptedException
:
- 传递异常(可能在执行某个特定于任务的清除操作之后),从而使你的方法也可以成为可中断的阻塞方法。
- 恢复中断状态,从而使调用栈中的上层代码能够对其进行处理。
二、取消与中断
通常,中断是实现取消的最合理方式。
(1)取消 Thread.stop
结果如下:
i = 1 j = 0
说明:当调用Thread.stop
时候,线程就直接退出了,没有执行 ++j
(2)中断 Thread.interrupte
结果:
三、中断几种方式
(1)取消标志 volatile
(2)Thread.interrupt()
(3)使用 Future
来取消
四、阻塞队列处理
背景:
生产者线程生成素数,并将它们放入一个阻塞队列。
如果生产者的速度超过了消费者的处理速度,队列将被填满,put
方法也会阻塞。
这时候:
当生产者在put
方法中阻塞时,如果消费者希望取消生产者任务,那么将发生什么情况?
(1)使用标识状态 volatile
它可以调用 cancel
方法来设置 cancelled
标识,
但此时生产者却永远不能检查这个标志,
因为它无法从阻塞的 put
方法中恢复过来(因为队列一直是满的,没有唤醒它)
这就导致结果:
任务可能永远不会检查取消标志,因此永远不会结束。
(2)使用中断
每次迭代循环中,有两个位置可以检测出中断:
- 在阻塞的
put
方法调用中
queue.put()
- 循环开始处查询中断状态
(!Thread.currentThread().isInterrupted())