关于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循环来轮询线程是否中断,若中断则线程正常结束。但结果并未按照我预料的方向发展。

执行结果:

关于java线程中断的几点发现sleep()、wait()等JDK内置的方法抛中断异常后会清掉线程的中断状态

由结果可以看出,sleep()方法有抛出线程中断异常,但我的轮询并未能检测到线程中断标志,从而导致线程未能结束。

但是如果将sleep()方法注释掉后线程就能正常结束

关于java线程中断的几点发现sleep()、wait()等JDK内置的方法抛中断异常后会清掉线程的中断状态

 后来经实验发现,原来JDK中sleep()、wait()等会抛中断异常的方法在抛出异常之前会清除线程的中断标识,所以上述bug代码才轮询不到线程的中断状态,解决的方法就是在catch里手动再将线程中断

关于java线程中断的几点发现sleep()、wait()等JDK内置的方法抛中断异常后会清掉线程的中断状态

其实不止这些抛中断异常的方法,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();
}

结果:

关于java线程中断的几点发现sleep()、wait()等JDK内置的方法抛中断异常后会清掉线程的中断状态

 题外话:

由上述bug还可以发现,thread.interrupt()方法并不是真正结束线程,而是将线程的置为中断状态,由用户主动去感知这个状态,然后决定需不需要结束线程。

另外像InputStream.read()这样的阻塞方法是不会去检测线程的中断状态的,如果它们一直处于阻塞状态,就算调用N次thread.interrupt(),线程依然不会结束。