关闭线程的正确方式?

最近无意间看到一些面试题,提到如何关闭一个线程。然后就在想,关闭线程不就是调用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的内存模型:
关闭线程的正确方式?_第1张图片
每个线程都有自己的一个工作内存,工作内存中存储着主内存变量的副本。当工作内存的变量发生改变后,会重新写回到主内存。问题就出在这里,上述程序中主线程和子线程都持有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()方法?

你可能感兴趣的:(Java进阶)