越努力,越幸运

朱季谦

腾讯云社区2022年度最佳作者、阿里云专家博主认证

安全优雅地停止线程

首先,先抛出一个问题,该如何安全而优雅地停止线程?

这道问题的背后,可以很小,小到只是简单终止一个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并发编程的艺术》

posted @ 2021-12-05 00:09  朱季谦  阅读(590)  评论(0编辑  收藏  举报