关闭线程的正确方式?

Posted this.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关闭线程的正确方式?相关的知识,希望对你有一定的参考价值。

最近无意间看到一些面试题,提到如何关闭一个线程。然后就在想,关闭线程不就是调用Thread.stop()的方法就好了吗?现在看来还真是有些问题了。翻了下Effective java那本书,了解到原来stop方法在很久之前就不提倡使用,因为这个方法是不安全的。
那么,要怎么去关闭一个正在执行的线程呢?一开始就想到用一个boolean变量去控制:

public class ThreadExample extends Thread 
    //标识线程是否结束
    public static boolean isDestroy = false;

    public static void main(String[] args) 
        ThreadExample t = new ThreadExample();
        t.start();
        try 
            //先让线程跑起来
            Thread.sleep(1000);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        //结束线程
        isDestroy = false;
    


    @Override
    public void run() 
        int i = 0;
        while (!isDestroy) 
            i++;
        
    

用上述的方式运行后,会发现程序怎么也不会结束,线程一直都在运行着。原因在哪?主要还是因为java的内存模型决定的,下图为Java的内存模型:

每个线程都有自己的一个工作内存,工作内存中存储着主内存变量的副本。当工作内存的变量发生改变后,会重新写回到主内存。问题就出在这里,上述程序中主线程和子线程都持有isDestroy变量的副本,当主线程修改这个值后,子线程的工作内存中isDestroy变量并没有刷新,依然是false,所以线程没有停止。解决的方法就是将该变量用volatile修饰。

public static volatile boolean isDestroy = false;

重新运行程序,发现程序能够正常终止了。原因在于volatile 关键字能够是该变量对其他线程“可见”,即当主线程修改变量并刷新到主内存后,会让其他线程去主内存中读取该变量。当然,volatile并不能保证线程的安全性。
另外,还有一种方式也能够终止线程,那就是通过interrupt()方法,该方法用于中断正在运行的线程,这种方式要分两种情况:
1. 线程没有阻塞的情况

public static void main(String[] args) 
        ThreadExample t = new ThreadExample();
        t.start();
        try 
            Thread.sleep(1000);
         catch (InterruptedException e) 
            e.printStackTrace();
        
        t.interrupt();
    
    @Override
    public void run() 
        int i = 0;
        //判断是否中断
        while (!isInterrupted()) 
            i++;
        
    

2.线程有阻塞的情况出现,可能由wait(),join(),sleep()方法造成,在上述循环中加入sleep方法:

while (!isInterrupted()) 
        try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
         i++;

运行后会发现抛出了异常:

java.lang.InterruptedException: sleep interrupted

原因在于interrupt方法用于中断正在运行的线程,线程处于阻塞态,就会抛出异常。只需要在捕获到异常后退出终止线程即可:

while (!isInterrupted()) 
        try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                break;
            
         i++;

现在知道了如何安全的终止一个线程,那么stop方法到底哪里不安全呢?查阅资料后发现stop方法会释放该线程所拥有的锁,加锁是为了保证数据一致性,突然解锁可能会导致数据的破坏,从而造成程序的严重问题。关于stop如何会使数据出现问题,可以参考下这篇博客:为什么不能使用Thread.stop()方法?

以上是关于关闭线程的正确方式?的主要内容,如果未能解决你的问题,请参考以下文章

正确关闭线程池:shutdown 和 shutdownNow 的区别

一文教你安全的关闭线程池

在应用程序关闭期间正确关闭一个可能运行很长时间循环的线程[重复]

Java避坑指南:finally块的陷阱及正确的关闭资源方式小

Java避坑指南:finally块的陷阱及正确的关闭资源方式小

Java避坑指南:finally块的陷阱及正确的关闭资源方式小