关于java线程中断的几点发现sleep()、wait()等JDK内置的方法抛中断异常后会清掉线程的中断状态
废话不多说,先贴上bug代码
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//轮询线程等待线程中断
while (!Thread.currentThread().isInterrupted()) {
Thread.yield();
}
System.out.println("dead...");
}
});
thread.start();
//中断线程
thread.interrupt();
}
在此程序中,我的本意是通过while循环来轮询线程是否中断,若中断则线程正常结束。但结果并未按照我预料的方向发展。
执行结果:
由结果可以看出,sleep()方法有抛出线程中断异常,但我的轮询并未能检测到线程中断标志,从而导致线程未能结束。
但是如果将sleep()方法注释掉后线程就能正常结束
后来经实验发现,原来JDK中sleep()、wait()等会抛中断异常的方法在抛出异常之前会清除线程的中断标识,所以上述bug代码才轮询不到线程的中断状态,解决的方法就是在catch里手动再将线程中断
其实不止这些抛中断异常的方法,Thread.interrupted()方法在获取到线程的中断状态后也会重置中断状态,而isInterrupted()方法则不会,因此,我们可以利用Thread.interrupted()来模拟抛异常前清除中断状态的行为:
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
block();
} catch (InterruptedException e) {
System.out.println("isInterrupted:" + Thread.currentThread().isInterrupted());
}
System.out.println("dead...");
}
});
thread.start();
//中断线程
thread.interrupt();
}
public static void block() throws InterruptedException {
//轮询线程等待线程中断
while (!Thread.currentThread().isInterrupted()) {
Thread.yield();
}
//抛异常之前重置线程中断状态
System.out.println("static:" + Thread.interrupted());
throw new InterruptedException();
}
结果:
题外话:
由上述bug还可以发现,thread.interrupt()方法并不是真正结束线程,而是将线程的置为中断状态,由用户主动去感知这个状态,然后决定需不需要结束线程。
另外像InputStream.read()这样的阻塞方法是不会去检测线程的中断状态的,如果它们一直处于阻塞状态,就算调用N次thread.interrupt(),线程依然不会结束。