Java线程中断的原理
2018-12-04 本文已影响15人
tracy_668
在java中不提供抢占式中断,也就是说不允许线程之间的直接抢占,(已经有过Thread.stop,Thread.suspend等API都已经被废弃, 容易导致各种问题。。) ,java提供的是协作式中断, “协作“是指在中断发生时,系统(JVM)会改变中断标记,但并不强制应用程序中断。对应用程序来说,需要主动检查中断标记,并最终决定是否中断。 所以完成这类中断,需要系统和应用程序共同”协作“。 此外,线程阻塞时,系统发起的InterruptedException也可以看做一种强制式的中断。因为,在这种情况下,不管线程是否同意,系统都会强制解除线程阻塞,可以简单理解为就是轮询某个表示中断的标记,我们在任何普通的代码中都可以实现,例如:
volatile bool isInterrupted;
//…
while(!isInterrupted) {
compute();
}
上述的代码问题也很明显。利用轮询检查标志变量的方式,在wait和sleep等线程阻塞操作中,无法感知中断,常常需要再sleep,wait过程中能感知发生了中断,显然我们自己实现的代码无法实现。要想让中断及时被响应,必须在虚拟机底层进行线程调度时对标记变量进行检查,JVM中确实是这样做的。下面摘自java.lang.Thread的源代码:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
//…
private native boolean isInterrupted(boolean ClearInterrupted);
可以发现,isInterrupted被声明为native方法,取决于JVM底层的实现。 实际上,JVM内部确实为每个线程维护了一个中断标记。但应用程序不能直接访问这个中断变量,必须通过下面几个方法进行操作:
public class Thread {
//设置中断标记
public void interrupt() { ... }
//获取中断标记的值
public boolean isInterrupted() { ... }
//清除中断标记,并返回上一次中断标记的值
public static boolean interrupted() { ... }
...
}
通常情况下,调用线程的interrupt方法,并不能立即引发中断,只是设置了JVM内部的中断标记。因此,通过检查中断标记,应用程序可以做一些特殊操作,也可以完全忽略中断。 在执行涉及线程调度的阻塞调用时(例如wait、sleep和join),如果发生中断,被阻塞线程会“尽可能快的”抛出InterruptedException。因此,我们就可以用下面的代码框架来处理线程阻塞中断:
try {
//wait、sleep或join
}
catch(InterruptedException e) {
//某些中断处理工作
}
所谓“尽可能快”,应该就是JVM在线程调度调度的间隙检查中断变量,速度取决于JVM的实现和硬件的性能。