欢迎大家搜索“小猴子的技术笔记”关注我的公众号,有问题可以及时和我交流。
中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断。在日常开发中,经常使用中断标识来进行线程的安全终止。
Thread 给我们提供了三个有关中断的方法“interrupt()","isInterrupted()"和"interrupted()”。那么它们的具体功能到底是怎么样子的呢?下面就来一起研究下吧。
Thread类中的interrupt()不能中断在运行中的线程,它只能改变中断状态。
public class InterruptExample implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("running......");
}
}
}
public class InterruptTest {
public static void main(String[] args) {
Thread thread = new Thread(new InterruptExample());
thread.start();
thread.interrupt();
}
}
如果你运行上面的线程示例,你会发现,控制台会一直循环输出如下的结果:
running......
running......
running......
很显然,想要通过“interrupt()”方法终止线程是做不到的。至于为什么?我们来看看“interrupt()”底层为我们做了什么。
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
}
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
// Just to set the interrupt flag
interrupt0();
b.interrupt(this);
return;
}
}
interrupt0();
}
可以看到调用了“interrupt()”之后,会先进行一个判断,判断执行中断的线程,和当前正在运行的线程是不是不相等。这里呢,可能有点绕,我画个图给大家加深一下理解:
判断完毕之后会触发一个安全检查,这个安全检查的目的是判断当前线程是否有权限修改中断标识。接着就是锁着一个对象,防止多线程引发的安全问题。最后判断线程是不是可中断的,如果是的话就设置中断标识然后返回。
在这里,也许你会迷惑为什么要做上述的判断? 那是因为“interrupt()”对线程的影响与线程的状态和在进行的IO操作有关(我们稍后会讲)。
通过分析源码可以看出,最终调用的还是一个底层的“interrupt0()”这个方法。其实通过阅读方法上的注释也很容易理解,这里的“interrupt()”方法并不会干扰线程的运行,它仅仅是设置了一个中断的标识。
private native void interrupt0();
上面说到了“interrupt()”方法对线程的影响与线程的状态和进行的IO操作有关。第一个展示的示例,它是没有IO操作的运行中的线程,因此调用“interrupt()”仅仅是设置了一个中断标识位,并不影响原来线程的运行。
在继续介绍“interrupt()”方法对线程的影响与线程的状态和进行的IO的其他影响之前,先来看一下判断线程中断标志的方法。
public boolean isInterrupted() {
return isInterrupted(false);
}
private native boolean isInterrupted(boolean ClearInterrupted);
通过调用“isInterrupted()”会对线程进行一个判断,判断调用的线程是否已经发生了中断。它会调用底层的"isInterrupted()"方法来对线程的中断标志进行判断。
在对线程是否已经中断的判断的同时还可以决定是否擦除线程的中断标识。Thread的“isInterrupted()”调用的是"isInterrupted(false)"表示不会擦除中断标识,也就是原来中断标识是啥样子调用了“isInterrupted()”之后还是啥样子。
由此可以明白:Thread类中的“isInterrupted()”对线程中断标识进行判断,如果已经发生了中断则为true,否则为false。
回到之前的话题:在线程的其他状态下,调用“interrupt()”或有什么情况发生?把上面的例子进行修改之后,查看一下输出的结果:
public class InterruptExample implements Runnable {
@Override
public void run() {
while (true) {
}
}
}
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new InterruptExample());
thread.start();
thread.sleep(200);
System.out.println("未调用interrupt前中断标志:" + thread.isInterrupted());
System.out.println("开始调用interrupt()");
thread.interrupt();
System.out.println("调用interrupt后中断标志:" + thread.isInterrupted());
thread.interrupt();
}
}
未调用interrupt前中断标志:false
开始调用interrupt()
调用interrupt后中断标志:true
可以看到,线程的中断标志已经被清除(即设置为false)。其实通过阅读源码给的注释,可以发现如果线程处于以下状态都会引起中断标志位的清除。
wait(),wait(long),wait(long, int)
join(),join(long),join(long, int)
sleep(long),sleep(long, int)
如果是阻塞的IO线程操作的话,那么IO的channel将会被关闭,中断标志将会被设置并且会抛出一个“java.nio.channels.ClosedByInterruptException”异常信息。
如果是一个阻塞的NIO的话,中断标志将会被设置,并且立即从“selection operation”中返回,可能具有非0的值。
如果非上述的情况,则设置线程的中断状态。如果线程还没有启动,则调用此方法不会有任何影响。
“interrupted()”是个Thread的static方法,用来恢复当前中断状态。
public class InterruptExample implements Runnable {
@Override
public void run() {
}
}
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new InterruptExample());
thread.start();
System.out.println("未调用interrupt时候的中断标识:" + Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
System.out.println("调用了interrupt时候的中断标识:" + Thread.currentThread().isInterrupted());
Thread.interrupted();
System.out.println("调用了Thread.interrupted()时候的中断标识:" + Thread.currentThread().isInterrupted());
}
}
未调用interrupt时候的中断标识:false
调用了interrupt时候的中断标识:true
调用了Thread.interrupted()时候的中断标识:false
我希望到这里你的头脑还是清晰的,因为“interrupted()”这个方法是Thread的static方法,它指的是当前线程是否被中断。这里的当前线程指的就是调用thread线程的main线程!!!
也许当前线程之前可能发生过中断,但是只要调用了“interrupted()”这个方法,就给它重新设置为false。
欢迎大家搜索“小猴子的技术笔记”关注我的公众号,有问题可以及时和我交流。