Thread的stop()有多危险

既然我们不能使用stop方法停止运行中的线程,那怎么样才能安全的终止一个正在运行的线程呢?
答案就是使用自定义的标志位来判断线程的执行情况,话不多说,我们上代码:

public class SafeStopThread implements Runnable {
    //变量必须使用volatile修饰
    private volatile boolean stop = false;
    int a = 0;

    @Override
    public void run() {
        while (!stop) {
            synchronized ("") {
                String name = Thread.currentThread().getName();
                System.out.println(name + ":开始时\ta=" + a);
                a++;
                try {
                    System.out.println(name + "线程内休眠1000\ta=" + a);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("异常处理");
                }
                a--;
                System.out.println(name + ":结束线程后\ta=" + a);
            }
        }
    }

    public void terminated() {
        stop = true;
    }
}

所有的主要逻辑和上期文章中相同,只是我们加入了一个判断线程是否终止的标志位stop,并且该属性使用了“volatile”修饰符修饰,因为volatile是设置属性的可见性,一旦我们的共享变量被volatile修饰后,它的修改值都会被更新到主存,当其他线程来读取该属性的时候,就会从主存中读取新值。
修改后的线程逻辑为,执行线程的时候判断是否已经被停止,如果停止了,则不去执行同步代码块,并且我们提供了设置结束线程标志位的方法“terminated()”。
接下来看看我们的测试方法,主要逻辑还是与之前的文章里代码逻辑一样,只是我们把线程的stop方法替换为设置结束标志位的“terminated()”方法,如下

public static void main(String[] args) throws InterruptedException {
        SafeStopThread safeStopThread = new SafeStopThread();
        Thread thread = new Thread(safeStopThread);
        thread.start();
        for (int i = 0; i < 5; i++) {
//            //不共享变量, 不影响
//            UnSafeThread thread2 = new UnSafeThread();
//            new Thread(thread2).start();
            //新的线程会共享变量
            new Thread(thread).start();
        }
        Thread.sleep(100);
        safeStopThread.terminated();
    }

只做了一下简单修改后,我们的程序就能安全执行啦,如下:

(SafeStopThread.java:3)
Thread-0:开始时	a=0
Thread-0线程内休眠1000	a=1
Thread-0:结束线程后	a=0
Thread-5:开始时	a=0
Thread-5线程内休眠1000	a=1
Thread-5:结束线程后	a=0
Thread-4:开始时	a=0
Thread-4线程内休眠1000	a=1
Thread-4:结束线程后	a=0
Thread-3:开始时	a=0
Thread-3线程内休眠1000	a=1
Thread-3:结束线程后	a=0
Thread-1:开始时	a=0
Thread-1线程内休眠1000	a=1
Thread-1:结束线程后	a=0
Thread-2:开始时	a=0
Thread-2线程内休眠1000	a=1
Thread-2:结束线程后	a=0

怎么样,是不是很神奇,妈妈再也不用担心我的代码丢啦!
细心的小伙伴可能自己去学习线程,然后查看了官方API,发现了原来官方提供了一个中断线程的方法叫做“interrupt()”的,那咱们能不能用呢?
那我们就来试一下吧!代码如下:

public static void main(String[] args) {
        Thread thread  = new Thread(){
            @Override
            public void run() {
                //线程不停止
               while (!isInterrupted()){
                   System.out.println("Running …………");
               }
            }
        };
        thread.start();
        System.out.println(thread.isInterrupted());
        thread.interrupt();
        System.out.println(thread.isInterrupted());
    }

可以看到,我们在线程开始后执行了“interrupt()”方法,但是线程并没有停止,因为它只是改变了线程的中断标志位,但是线程并没有因此停止。那既然这样的话,我们不是拿到这个状态,对它进行判断就可以做到线程安全停止的代码了呢?
所以小白丁尝试着做了一个这样的代码实验,如下:

class SafeStopThreadByInterruptedTest {
    public static void main(String[] args) throws InterruptedException {
        SafeStopThreadByInterrupted thread = new SafeStopThreadByInterrupted();
        thread.start();
        //定义计时器,0.5秒后线程停止
        new Timer(true).schedule(new TimerTask() {
            @Override
            public void run() {
                thread.interrupt();
            }
        },500);
    }
}

class SafeStopThreadByInterrupted extends Thread {

    @Override
    public void run() {
        while (!isInterrupted()) {
            System.out.println("Running …………");
        }
    }
}

所以我们可以借助“interrupt()”方法来编写更加简洁、安全的线程终止代码

当然了,我们还有很多线程池可以使用,借助他们的shutdown方法逐步关闭线程池中的线程,这样的方式是一种安全的关闭方法,不会产生类似stop方法的问题。