线程中断就是处于运行状态的线程被强制打断。线程中断总体来说有三种方法:正常退出、stop暴力停止、interrupt异常停止。其中使用stop()方法是不安全的,可能导致数据不同步或者资源无法回收,目前stop()方法已经被标注为作废方法。一般使用interrupt停止线程,这里有几个与之相关的方法:
public void interrupt() {} // 中断线程
public boolean isInterrupted(){} // 查看线程中断状态
我们来使用一下看看效果:
class MyThread7 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
public class Interrupt {
public static void main(String[] args) throws InterruptedException {
MyThread7 thread = new MyThread7();
thread.start();
System.out.println(thread.getName() + "线程的中断状态:" + thread.isInterrupted());
thread.interrupt(); // 由主线程中断了一个子线程
System.out.println(thread.getName() + "线程的中断状态:" + thread.isInterrupted());
}
}
多运行几次会发现,run()方法中的100次循环全部执行完了,线程好像并没有中断,并且可以看到打印的线程状态是不同的:
Thread-0线程的中断状态:false
Thread-0线程的中断状态:true
打印的线程状态有所变化,但是线程好像又没有中断,这是为什么呢?实际上,interrupt并不能真正的停止线程,只是更换了线程状态标志。
在Core Java中有这样一句话:"没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 "。这句话启发了我们如何正确的中断一个线程,中断一个线程的本质就是中断run()方法的执行,也就是说run()不再执行后,这个线程就结束了。所以,通过中断状态标志位来控制run()方法中逻辑代码的运行,就可以很好的保证线程的中断。比如这样做:
public void run(){
try{
....
while(!Thread.currentThread().isInterrupted() && more work to do) {
// do more work;
}
} catch(InterruptedException e) {
// thread was interrupted during sleep or wait
} finally {
// cleanup, if required
}
}
这里的while循环不断的检查中断状态,一旦状态转为true,也就是执行了interrupt方法,那么逻辑代码不再执行。
上面所说的情况是线程在运行状态时被中断所要做的处理,那么如果线程在阻塞状态被中断会怎样?我们在sleep方法中看到过,它抛出了一个InterruptedException,当一个线程使用了sleep方法(wait、join)处于阻塞状态时,再使用interrupt就会触发sleep方法抛出InterruptedException异常(实际上是给阻塞的线程发出中断信号,阻塞线程检查到中断标识),进而导致线程中断。比如这样做:
class MyThread7 extends Thread {
@Override
public void run() {
try {
Thread.sleep(20000); // 子线程睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Interrupt {
public static void main(String[] args) throws InterruptedException {
MyThread7 thread = new MyThread7();
thread.start();
thread.interrupt(); // 由主线程中断了一个子线程
}
}
补充:interrupted()方法,作用是测试当前线程是否已经中断,线程的中断状态也是由该方法清除。
public class Interrupt {
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName() + "线程的中断状态:" + Thread.currentThread().interrupted());
System.out.println(Thread.currentThread().getName() + "线程的中断状态:" + Thread.currentThread().interrupted());
}
}
main线程的中断状态:true
main线程的中断状态:false
主线程被打断,使用interrupted()方法查看中断状态,发现标志位改为了true;再次使用interrupted()查看中断状态,发现标志位改为false。在第一次使用interrupted()方法时,它返回标志位,同时清除标志位(置为false),导致下一次的返回值变为false。所以interrupted()方法有清除标志位的功能。