关闭线程的正确方式?
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块的陷阱及正确的关闭资源方式小