安全优雅地停止线程
首先,先抛出一个问题,该如何安全而优雅地停止线程?
这道问题的背后,可以很小,小到只是简单终止一个Thread线程,也可以很大,大到例如Dubbo应用的优雅下线......它们其实都有一个共同之处,即非一刀断式地暴力停止某个进程或者线程,而是能够实现在终止过程中,有机会去清理资源,跑完剩余的任务,最后没有任何资源在运行了,才做最后结束,这才算安全而优雅地停止。
在Java多线程当中,停止线程的方法,其中,有一个已经过期而不建议使用的方式stop(),它停止线程的方式比较简单粗暴,不保证线程的资源正常释放就直接停止了,也就意味着,可能还有线程正在跑,没有运行完成,就直接终结了,这可能会导致程序出现不确定的状态,即死锁状态。
以stop方式终结线程的方法已经过期,即不再建议使用。
那么,可有其他方式来优雅地结束线程运行吗?
这里,可以通过interrupt()方法间接实现。
为什么说是简接实现呢?
因为线程执行interrupt()方法并不会直接就终止线程。
接下来,就简单分析一下,interrupt()是如何实现安全而优雅地终止线程的。
首先,当执行线程的interrupt()方法后,就会给该线程打上一个中断的标识属性,该标识属性原本是false的,但被打上中断标识后,就会变成true了,这里有点类似volatitle变量的可见性玩法,通过这样的可见性变量,我们就可以设置某种状态,当满足该状态时,就可以跳出程序,提前结束。
可以通过isInterrupted()方法获取到中断标识属性的状态值,若是true,表示该线程已经被打上中断标识,那么,就可以先清理完资源后,再结束该线程。
然而,需要注意一点是,这里有一个类似的静态方法,Thread.interrupted(),该方法也可以获取到线程中断状态,但遗憾的是,这个interrupted方法在判断一次线程是否中断后,就会立即对该线程的中断状态复位,即恢复线程到非中断的状态。除此之外,声明抛出InterruptedException的方法,在抛出异常前,也会通过虚拟机将该线程的中断标识状态清除,然后再抛出异常,这时再调用isInterrupted()方法返回的是false。
这里以代码验证一下——
public static void main(String[] args) throws InterruptedException {
Runner one = new Runner();
Thread countThread = new Thread(one,"CountThread");
//启动线程
countThread.start();
//沉默一秒,先让线程CountThread执行1秒
TimeUnit.SECONDS.sleep(1);
//通过interrupt()方法对线程countThread设置中断标识
countThread.interrupt();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
//当countThread线程标识中断时,Thread.currentThread().isInterrupted()返回的是true,即可结束该线程,同时,停止资源i++的继续运行
while (!Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("Count i = " + i);
}
}
前边提到过,interrupt()标识中断位的玩法,很类似volatitle变量的可见性,反过来,volatitle某种程度上也可以替代interrupt()来判断线程是否需要中断,类似代码如下——
public static void main(String[] args) throws InterruptedException {
Runner two = new Runner();
Thread countThread = new Thread(two,"CountThread");
countThread.start();
//睡眠1秒
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on){
i++;
}
System.out.println("Count i = " + i);
}
public void cancel(){
on = false;
}
}
参考《Java并发编程的艺术》