一、Thread.stop() VS Thread.interrupt()
在jdk1.0时代,要终止一个Java线程,可以使用Thread提供的stop()和destroy()等方法,但这些方法在jdk1.4之后就已经不推荐使用了,原因是这些方法会强行关闭当前线程,并解锁当前线程已经持有的所有监视器(互斥锁、共享锁),这会导致被这些监视器保护的数据对象处于不一致的状态,其它线程可以查看到这些不一致状态的数据对象,从而导致各种不可预知的错误。
在jdk1.4引入了另外一种结束线程的方式——中断。简单来说,就是每个线程有一个int类型的成员变量_interrupted(0表示没被中断,1表示被中断)。线程在执行过程中的适当位置,检查这个变量,来获知当前线程是否应该结束。
我们可以使用Thread.interrupt()方法将中断标记_interrupted设为1,也可以使用Thread.interrupted()方法将中断标记_interrupted重置为0。下面是从jdk的Thread.java类中截取的interrupt()源码:
// java.lang.Thread
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();
}
private native void interrupt0(); //native方法
可以看到,Thread.interrupt()方法的核心方法是Thread.interrupt0()方法,从注释上看,这个方法只会设置中断标记位(_interrupted变量)。在调用Thread.interrupt()方法之前,如果当前线程已经处于阻塞状态(比如调用了Thread.sleep()方法),那么调用该阻塞线程的Thread.interrupt()方法将导致当前线程从Thread.sleep()函数中醒来,并抛出InterruptedException。下面是Thread.sleep()和Thread.interrupt()的示例:
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
TimeUnit.HOURS.sleep(1);//当前线程sleep一个小时
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
try {
TimeUnit.SECONDS.sleep(5);//主线程sleep5秒钟,保证thread线程能够执行sleep方法
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();//主线程中断thread线程,导致sleep方法抛出InterruptedException,结束阻塞
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("finish");
}
在这个示例中,thread线程执行Thread.sleep()方法就标志着,该线程将在未来1个小时内不参与cpu的竞争,thread线程将处于阻塞状态。如果说Thread.interrupt()方法只是修改了中断标记_interrupted的值,那么已经放弃cpu、处于阻塞状态的thread线程如何能感知到这个变量已经被改变,从而立即抛出InterruptedException呢?为了回答这个问题,我们需要深入native方法Thread.interrupt0()。
二、Thread.interrupt0()源码跟踪
1、jdk源码(JNI注册)
// jdk/src/share/native/java/lang/Thread.c:43
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep}, //sleep方法名映射成的jni方法名
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},