Thread的中断机制
Thread的中断机制(interrupt)
一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果比如会带着自己所持有的锁而永远的休眠,迟迟不归还锁等。 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了.
中断线程的使用场景:
在某个子线程中为了等待一些特定条件的到来, 你调用了Thread.sleep(10000), 预期线程睡10秒之后自己醒来, 但是如果这个特定条件提前到来的话, 来通知一个处于Sleep的线程。又比如说.线程通过调用子线程的join方法阻塞自己以等待子线程结束, 但是子线程运行过程中发现自己没办法在短时间内结束, 于是它需要想办法告诉主线程别等我了. 这些情况下, 就需要中断.
线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前工作,并转而执行其他的工作。
中断是通过调用Thread.interrupt()方法来做的. 这个方法通过修改了被调用线程的中断状态来告知那个线程, 说它被中断了. 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true; 对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为true.但调用Thread.interrupted()会对中断状态进行复位。
每个线程都有一个boolean类型的中断状态,当中断线程时,这个线程的中断状态将被置为true,在Thread中包含了中断线程以及查询线程中断状态的方法。
1 | public class Thread |
interrupt方法能中断线程,isInterrupted方法能返回目标线程的中断状态,静态的interrupted方法能消除当前线程的中断状态并返回它之前的值。这也是清除中断状态的唯一方法。
1 | public class InterruptDemo { |
线程的t.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。
判断线程是否被中断
判断某个线程是否已被发送过中断请求,请使用Thread.currentThread().isInterrupted()方法(因为它将线程中断标示位设置为true后,不会立刻清除中断标示位,即不会将中断标设置为false),而不要使thread.interrupted()(该方法调用后会将中断标示位清除,即重新设置为false)方法来判断,下面是线程在循环中时的中断方式:
1 | while(!Thread.currentThread().isInterrupted() && more work to do){ |
如何中断线程
如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法(sleep、join、wait、1.5中的condition.await及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。
调用interrupt并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的信息。
对中断操作的正确理解
中断操作并不会真正的中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己,这些时刻也被称为取消点,有些方法,例如wait,sleep,join等,将严格的处理这种请求,当他们收到中断请求或者在开始执行时发现某个已经设置好的中断状态时,将抛出一个异常。
没有任何语言方面的需求一个被中断的线程应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断。某些线程非常重要,以至于它们应该不理会中断,而是在处理完抛出的异常之后继续执行,但是更普遍的情况是,一个线程将把中断看作一个终止请求,这种线程的run方法遵循如下形式:
1 | public void run(){ |
上面是while循环在try块里,如果try在while循环里时,因该在catch块里重新设置一下中断标示,因为抛出InterruptedException异常后,中断标示位会自动清除,此时应该这样:
1 | public void run() { |
另一个例子:
1 | class Mythread implements Runnbable |
上述程序中,一个线程启动之后就进入了休眠状态,原本是要休眠10s再继续执行,但是主方法在线程启动之后的两秒就将其中断,休眠一旦中断之后将执行catch中的代码。catch中的代码就是线程自己决定应该做一些什么操作,是继续执行还是什么都由自己决定。
Thread.sleep() , 。Thread.wait() , t.join() 都要用trycatch包起来。进行中断的处理。
Author: corn1ng
Link: https://corn1ng.github.io/2017/11/20/Thread的中断机制(interrupt)/
License: 知识共享署名-非商业性使用 4.0 国际许可协议