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方法的问题。