从停止线程说起

losetowin 发布于:2018-10-26 18:44 分类:Java  有 163 人浏览,获得评论 0 条 标签: volatile 停止线程 

本文地址:http://www.dutycode.com/anquan_tingzhi_xiancheng.html
除非注明,文章均为 www.dutycode.com 原创,欢迎转载!转载请注明本文地址,谢谢。

一般来我们这边面试的同学,这个问题大概率会被问到。至少,我认为这是一个比较好玩的问题,并且能引伸出来比较多的知识点。

而且,编写不当,很可能对线上造成意想不到的后果。


当然,这个不是我们今天讨论的重点,我们只讨论其中的一种实现的类似延伸。 


一般我们会在线程执行的时候,设置一个标志位,表示是否继续执行。如果我们打算停止线程的时候,将标志位设置为false,这样让程序可以停止掉。一般我们可能用类似下面的实现方式。

 

public class Demo implements Runnable{

    private boolean isRun = true;

    @Override

    public void run() {

        System.out.println("Run...");

        while(isRun){

        }

        System.out.println("Stop...");

    }

    public void stopRun(){

        this.isRun = false;

    }

    public static void main(String[] args) {

        Demo d = new Demo();

        new Thread(d).start();

        try {

            TimeUnit.SECONDS.sleep(1);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("停止线程...");

        d.stopRun();

    }

}


main方法里面,我们启动了一个线程,线程中在while循环前,会打印Run,如果设置isRun=false,期望run方法跳出while循环,打印Stop字符串。 


所以我们期望的结果应该类似下面这种:

Run...

停止线程...

Stop...


但真正执行的时候发现,结果可能和预想的不太一样,比如,我的电脑的运行结果如下:

Run...

停止线程...

Stop并没有打印,线程也没有被停止掉。


备注:运行环境(不同运行环境可能结果不同)

java version "1.8.0_172"

Java(TM) SE Runtime Environment (build 1.8.0_172-b11)

Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)

OS:Mac Osx 10.13.6

IDE:IDEA


我们期望通过设置标志位的时候停止正在运行的线程,但某些场景下线程并没有响应标志位。导致线程并没有按照预想的结束。


原因在于,我们设置的标志位,并没有真正被正在运行的线程读取到。也就是说,线程中依旧读取isRun的结果是true,而并不是改变之后的false值。 


JAVA线程运行的内存结构如下:


启动线程之后,线程会优先从线程自己的工作内存中读取数据,读取不到的话,会读取主存,之后将数据回写到自己的工作内存中。


所以,在我们上面的例子中,最开始isRun的值分别如下:


当我们主线程中设置isRun=false之后


所以,在我们的线程中,依旧读取的isRun=true,所以也就不会跳出while循环,进而执行Stop的打印了。 




修改的办法也简单,按照上面的思路,我们需要在isRun被设置成false的时候,Thread0线程也能知道isRun=false了。 


在Java中,volatile关键字可以强制让线程读取主存中的数据,所以当isRun被设置之后,Thread0读取的也就是false了。 也就是volatile关键字的内存可见性。


将isRun增加volatile参数即可

private volatile boolean isRun = true;


回到最开始的问题,用标志位的方式,我们容易漏掉volatile关键字,导致线程没有按预期停止。那还有什么其他方式停止线程呢?


    可以借助interrupt。


上面任务代码改成以下的方式:

public class Demo implements Runnable{

    @Override

    public void run() {

        System.out.println("Run...");

        while(!Thread.currentThread().isInterrupted()){

        }

        System.out.println("Stop...");

    }

    public static void main(String[] args) {

        Demo d = new Demo();

        Thread t = new Thread(d);

        t.start();

        try {

            TimeUnit.SECONDS.sleep(1);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("停止线程...");

        t.interrupt();

    }

}

执行结果和预期一样:

Run...

停止线程...

Stop...


版权所有:《攀爬蜗牛》 => 《从停止线程说起
本文地址:https://www.dutycode.com/anquan_tingzhi_xiancheng.html
除非注明,文章均为 《攀爬蜗牛》 原创,欢迎转载!转载请注明本文地址,谢谢。