Java如何停止一个线程
在Java中,停止一个正在运行的线程,我们可以通过如下方法实现:
- 设置一个volatile类型的变量,通过判断变量的值来确定run方法是否运行完成正常退出
- 通过interrupt()方法来通知,给线程打上一个终止的标志,但是具体的停止逻辑需要在线程内部实现
此外还有一种方法是调用stop()方法来停止线程,但是这个方法不安全:它会释放该线程持有的所有锁,一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。suspend方法:容易发生死锁,虽然调用suspend方法目标线程会停下来,但是仍然持有之前的锁,其他线程都不能访问锁定的资源,就会造成死锁,除非被挂起的线程恢复运行。
所以这里只提及了上述的两种方法。
volatile变量
我们可以自己实现的线程中设置了volatile的变量,然后在线程内部判断volatile是否满足运行的条件,满足就继续运行,不满足就退出线程。我们可以在调用线程的地方设置这个volatile变量,通过对volatile的设置达到停止线程的目的。
这里为什么使用volatile变量呢,因为volatile变量会保证多线程之间的可见性,变量的值一经修改所有的线程都能看到这个改变,根据这一性质才能停止线程的运行。
一个简单的例子:
public class ThreadInterrupt extends Thread{
private volatile boolean exit = false;
@Override
public void run() {
while (!exit) {
System.out.println("Running!");
}
}
public static void main(String[] args) throws Exception {
ThreadInterrupt thread = new ThreadInterrupt();
thread.start();
sleep(10);
thread.exit = true;
thread.join();
System.out.println("线程已退出!");
}
}
但是,使用volatile变量停止线程还是不够全面,某些情况下索然可用,但是某些情况下可能会有严重的问题。
Java并发编程实战指出了其缺陷:如果我们遇到了线程长时间阻塞(例如生产者消费者中就存在这样的情况),就没办法及时唤醒它,或者永远都无法唤醒该线程。此时我们就可以使用interrupt了,interrupt在设计之初就把wait等长阻塞作为一种特殊情况考虑在内,所以可以在线程内部有sleep或者wait方法时使用interrupt方法停止线程。
interrupt()方法
我们首先看看线程中断的几个方法:
- java.lang.Thread#interrupt():中断目标线程,给目标线程发出一个中断信号,线程被打上中断标记
- java.lang.Thread#isInterrupted():判断目标线程是否被中断,不会清除中断标记
- java.lang.Thread#interrupted():判断目标线程是否被中断,会清除中断标记
从上图源码可以发现,interrupted()方法和isInterrupted()方法都调用了本地isInterrupted()方法,该方法根据传入的参数决定是否清除中断状态,如果ClearInterrupted为true的话就要清除线程的中断标志,如果为false则不会清除。
使用interrupt暂停线程又有两种场景,第一种场景是线程的run方法中没有sleep和wait等会响应中断的方法,第二种场景就是有sleep和wait方法。我们首先来看看run方法中不包含sleep和wait方法的场景。
run方法不包含sleep和wait等响应中断的方法
我们首先看一个错误的示范。
首先我们看看打印Integ.MAX_VALUE/10以内的1000000倍数:
public static void main(String[] args) throws Exception {
int i = 0;
while (i < Integer.MAX_VALUE / 10) {
if (i