推荐:​​Java并发编程汇总​​

停止线程的正确姿势

原文地址

​​Java线程状态和关闭线程的正确姿势​​

正文

线程的run()方法正常执行完毕之后线程就正常死亡进入TERMINATED状态了,那么如果我们有中途停止线程的需求,我们应该如何正确的结束一个线程呢?

使用interrupt()方法

在线程内部,其定义了一个变量来标识当前线程是否处于被打断状态,调用interrupt()方法则使这个状态变为true;换句话说,线程在阻塞状态的时候会检查标识状态,如果是true,则抛出InterruptedException,否则阻塞,所以不会打断非阻塞的线程,即对正在运行的线程此方法无效。我们采用这个方法加异常处理的方式来结束一个线程。

public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
// 这里的return是必须的,原因后面说明
return;
}
System.err.println("thread interrupt test...");
});
thread.start();
thread.interrupt();
System.out.println("main thread end...");
}

停止线程的正确姿势_重置

这里关于线程中的打断标识变量(之后以interrupt称)需要说明的是,在特定的情况下其状态会被重置。

  1. 线程内部在catch了异常之后interrupt的状态会被重置为false。
  2. 线程调用了Thread.interrupted()方法之后,interrupt的状态会被重置为false。如果需要判断线程是否中断的话可以使用对象方法isInterrupted(),此方法不会重置。

所以在刚才的代码中需要加入return来结束线程,否则的话线程还是会继续往下执行,如下图:

停止线程的正确姿势_停止线程_02


使用isInterrupted()实现:

public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
int k = 0;
while (k++ < 10) {
System.out.println("do something..." + k);
}
}
System.err.println("thread end...");
});
thread.start();
Thread.sleep(1);
// 主线程流程执行完了,需要停止线程
thread.interrupt();
}

stop()方法——不正确的线程中断方法

在线程提供的方法中还有一个方法可以强制关闭线程——stop()。这个方法可以说是相当的霸道,给人一种“我不管,我就是要你现在立刻死亡(指线程)”的感觉,并且其还会释放线程所有的锁资源,这样可能会导致出现数据不一致从而出现线程不安全的情况,如下面例子。

public class Test {

public static volatile boolean flag = false;
public int state = 0;

public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread thread = new Thread(() -> {
synchronized (test) {
try {
test.state = 1;
Thread.sleep(100);
if (flag) {
test.state = 2;
}
System.err.println("thread execute finished...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(1);
thread.stop();
flag = true;
System.out.println("state状态:" + test.state);
}
}

在这段代码中,进入线程时默认将state赋为1,接着过一段时间后如果触发了特定条件则把state赋为2,但是在特定条件触发之前,线程就被终止掉了,这个特定条件虽然符合但却没办法执行,从而导致数据的不一致。

结果图:

停止线程的正确姿势_停止线程_03

所以,我们应该采用上面正确的方式而不是stop()来中止线程。此外,stop()方法若在线程start()之前执行,那么在线程启动的时候就会立即死亡。