从停止线程说起
losetowin 发布于:2018-10-26 18:44 分类:Java 有 3605 人浏览,获得评论 0 条 标签: volatile 停止线程
除非注明,文章均为 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
除非注明,文章均为 《攀爬蜗牛》 原创,欢迎转载!转载请注明本文地址,谢谢。