使用 interrupt 替代 stop 等方法

在编写多线程程序的时候,难免会碰到需要终止一个线程的情形。但是 Thread.stop() 等一系列方法是 Java 官方不赞成使用的,在一些 IDE 里使用这些方法会报方法被废弃的警告。而官方建议使用的方法是 Thread.interrupt() ,这个方法会通知线程中断,至于线程被通知后怎么做就要咱们自己实现了。

在线程中可以调用 Thread.isInterrupted() 方法来查看自己是否被通知。

下面上一个小 demo。

interrupt 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class InterruptTest implements Runnable {
public static void main(String[] args) {
Thread thread = new Thread(new InterruptTest());
thread.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("Thread state is set to interrupt");
}

@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("The thread is interrupted.");
} else {
System.out.println("The thread is NOT interrupted.");
}
}
}
}

运行一下,会发现程序并不会中断,强制停止程序后,在输出中可以看到如下内容:(前面和后面部分重复的已省略)

……
The thread is NOT interrupted.
The thread is NOT interrupted.
Thread state is set to interrupt
The thread is interrupted.
The thread is interrupted.

……

可以看到使用 Thread.interrupt() 方法只是改变了线程的状态,并没有真正地将线程中断或终止。

除了上面提到的两个方法,还有一个静态方法 Thread.interrupted() ,与 Thread.isInterrupted() 的区别在于获取当前线程是否被中断的同时还会清除中断的状态,这里就不演示该方法的用法了。

interrupt 实现死循环程序的终止

当我们需要在线程中实现一个死循环的程序的时候,通常强制停止一个线程是不安全的,因为线程中的 IO 流、网络请求等还没有被释放,所以一般的做法是将死循环的条件改为 !Thread.isInterrupt()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class InterruptTest implements Runnable {
public static void main(String[] args) {
Thread thread = new Thread(new InterruptTest());
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("Thread state is set to interrupt");
}

@Override
public void run() {
while (!Thread.currentThread().isInterrupted())
System.out.println("The thread is running.");
}
}

输出:

……

The thread is running.
The thread is running.
Thread state is set to interrupt

当你使用死循环完成一整个循环的时候,线程就去检测一次是否被通知可以中断,如果是的话,就退出循环,这时你就可以释放线程里所使用的 IO、网络请求等资源。

interrupt 终止 sleep

如果你在使用 Thread.sleep() 的时候观察过它抛出的异常,就不会对 Interrupt 这个单词眼生了。

如果你运行过上面的一些例程,你应该会觉得程序很难进行调试,因为当运行起来的时候程序会占满你的 CPU,使你很难强制终止它。所以在调试死循环程序的时候通常会写一些 sleep 来避免密集的 CPU 操作,但是上面咱并没有那么做,这也正与本节要讲的核心内容有关,因为 Thread.interrupt() 方法会终止 sleep ,并且不会改变线程的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class InterruptTest implements Runnable {
public static void main(String[] args) {
Thread thread = new Thread(new InterruptTest());
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("Thread state is set to interrupt");
}

@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Some operations.");
try {
Thread.sleep(9999999);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Operations finished.");
}
}
}

输出:

Some operations.
Thread state is set to interrupt
Operations finished.
Some operations.
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at test.InterruptTest.run(InterruptTest.java:21)
at java.lang.Thread.run(Thread.java:748)

可以看到 Thread.interrupt() 终止了 sleep 并且抛出了一个异常,而且线程依然在继续运行。

因为当线程处于 sleep 时,interrupt 只会使线程终止 sleep 并抛出 InterruptedException 异常,并不会通知线程停止。

那么当我们想要在循环结束的时候中断线程,就需要自己使用 boolean 变量来进行判断了。

当我们希望线程终止的时候,将 boolean 标记为 true ,并且调用 Thread.interrupt() 方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class InterruptTest extends Thread {
public static void main(String[] args) {
InterruptTest thread = new InterruptTest();
thread.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
thread.interrupt = true;
System.out.println("Thread state is set to interrupt");
}

public boolean interrupt = false;

@Override
public void run() {
while (!interrupt || Thread.currentThread().isInterrupted()) {
System.out.println("Some operations.");
try {
Thread.sleep(9999999);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Operations finished.");
}
}
}

输出:

Some operations.
Thread state is set to interrupt
Operations finished.
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at test.InterruptTest.run(InterruptTest.java:24)

虽然输出和上面是一样的,但是这次程序是自己正常退出,而不是我们强制结束的。