线程中断是什么
一个线程在未正常结束之前, 被强制终止是很危险的事情. 因为它可能带来完全预料不到的严重后果. 所以你看到Thread.suspend, Thread.stop等方法都被Deprecated了。那么不能直接把一个线程搞挂掉, 但有时候又有必要让一个线程死掉, 或者让它结束某种等待的状态 该怎么办呢? 优雅的方法就是, 给那个线程一个中断信号, 让它自己决定该怎么办
如何中断一个线程
Thread类的一个公共非静态方法
public void interrupt()
中断线程。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
我们通过翻译源码的Comments看看中断能做什么
1.如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。
2.如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。
3.如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。
4.如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。
5.如果以前的条件都没有保存,则该线程的中断状态将被设置。
6.中断一个不处于活动状态的线程不需要任何作用。
抛出:
SecurityException - 如果当前线程无法修改该线程
看完源码,很容易理解第一点,当你在当前线程执行interrupt时,如果不是当前线程,则会校验checkAccess
第二点,首先join方法能被中断的原因是wait被唤醒了,从join的源码就可以看出,至于wait和sleep方法能被唤醒,我认为是interrupt0完成的,因为只有这一个是native方法
第三点和第四点,是因为他们设置了blocker
用法经验
interrupt用途: unBlock操作,支持任务cancel, 数据清理等。
两条实践原则:
1.Don't swallow interrupts (别吃掉Interrupt,一般是两种处理: 继续throw InterruptedException异常。 另一种就是继续设置Thread.interupt()异常标志位,让更上一层去进行相应处理。
2.Implementing cancelable tasks with Interrupt (使用Thread.interrupt()来设计和支持可被cancel的task)
注册Interrupt处理事件
我们的应用程序要处理cancel任务,一般都是采取主动轮询Thread.isInterrupt()来检测是否中断,这种方式的不好之处是一方面对我们的业务代码存在嵌入性,另一方面就是存在延迟,线程不可能一直在检查点检查是否中断
所以我们可以编写一个自定义的实现Interruptible接口的处理类,然后在打断线程前通过
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn
的方式注入我们的处理类
当线程被打断时,会主动调用我们处理类中的interrupt方法
可中断的NIO就是通过这种方式实现的,参见AbstractInterruptibleChannel源码
package niube.thread;
import sun.nio.ch.Interruptible;
public class InterruptDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(myThread, new MyInterruptHandler());
myThread.start();
myThread.interrupt();
}
static class MyThread extends Thread {
static volatile boolean flag = true;
@Override
public void run() {
while (flag)
yield();
}
}
static class MyInterruptHandler implements Interruptible {
@Override
public void interrupt(Thread thread) {
if (thread instanceof MyThread) {
((MyThread) thread).flag = false;
System.out.println("thread is interrupted");
}
}
}
}