1.2.3 理解中断 interrupt()

Java 并发编程的艺术第2版学习笔记

原书:《Java 并发编程的艺术第2版》 | 作者:方腾飞 魏鹏 程晓明 | 2023 年 9 月 | 机械工业出版社

interrupt() 有什么作用?

  • 作用是向线程发送一个中断信号,这个信号告诉线程应该中断正在进行的操作。当线程接收到这个信号后,可以采取适当的方式来响应中断。
  • interrupt() 方法并不会立即停止线程。它仅仅是设置了线程的中断状态位,至于线程如何响应中断,需要开发者在代码中明确指定。

interrupt() 应用场景

响应中断以退出长时间运行或阻塞的任务

假设有一个线程正在执行一个耗时的任务,比如文件下载,你可能希望用户能够在需要时取消这个操作。可以通过 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
public class FileDownloader implements Runnable {
    private Thread currentThread;

    public void download(String file) {
        currentThread = new Thread(this);
        currentThread.start();
    }

    public void run() {
        // ... 文件下载逻辑

        // 在适当的时候检查是否被中断
        if (Thread.interrupted()) {
            // 清理资源, 停止下载等
            return;
        }

        // ... 继续下载
    }

    public void cancel() {
        // 发送中断信号
        currentThread.interrupt();
    }
}

当一个线程正在等待某个事件(如Object.wait()、Thread.join()或者Thread.sleep()),可以使用中断来通知它应该退出等待状态。这些方法将响应中断,并抛出InterruptedException。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class WaitingTask implements Runnable {
    public void run() {
        try {
            // 任务进入等待状态
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // 当其他线程调用 interrupt() 时,
            // InterruptedException 将被抛出,
            // 这里我们可以断定这个线程应该停止当前的操作
            Thread.currentThread().interrupt(); // 可选的重新设置中断状态
        }
    }
}

检查中断来优雅地终止线程

这种场景不限于长时间运行的任务,它也涉及到那些需要检查其中断状态以决定是否继续运行的任务。这可以包括 CPU 密集型任务轮询操作,或者只是普通的运行逻辑。线程会定期检查中断状态,以确保它可以在接收到中断信号后立即停止其工作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class MyThread extends Thread {

    @Override
    public void run() {
        while (!isInterrupted()) {
            // 执行线程任务
            System.out.println("MyThread is running...");
        }

        System.out.println("MyThread is interrupted...");
    }
}

为什么 SleepThread 没有被中断?

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Interrupted {

    public static void main(String[] args) {
        // sleepThread 不停的尝试睡眠
        Thread sleepThread = new Thread(new SleepRunner(), "SleepThread");
        sleepThread.setDaemon(true);
        sleepThread.start();

        // busyThread 不停的运行
        Thread busyThread = new Thread(new BusyRunner(), "BusyThread");
        busyThread.setDaemon(true);
        busyThread.start();

        // 休眠 5 秒,让 sleepThread 和 busyThread 充分运行
        SleepUtils.second(5);

        sleepThread.interrupt();
        busyThread.interrupt();

        System.out.println("SleepThread interrupted is " + sleepThread.isInterrupted());
        System.out.println("BusyThread interrupted is " + busyThread.isInterrupted());

        // 防止 sleepThread 和 busyThread 立刻退出
        SleepUtils.second(2);
    }

    static class SleepRunner implements Runnable {
        @Override
        public void run() {
            while (true) {
                SleepUtils.second(10);
            }
        }
    }

    static class BusyRunner implements Runnable {
        @Override
        public void run() {
            while (true) {
            }
        }
    }
}

输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
> Task :Interrupted.main()
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:337)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at com.wyyl1.study.chapter1.SleepUtils.second(SleepUtils.java:8)
	at com.wyyl1.study.chapter1.Interrupted$SleepRunner.run(Interrupted.java:33)
	at java.base/java.lang.Thread.run(Thread.java:833)
SleepThread interrupted is false
BusyThread interrupted is true

SleepThread 确实被中断过。这是由 InterruptedException 的抛出证实的,这个异常在 SleepUtils.second(10) 内部的 Thread.sleep() 方法调用时被抛出,因为 sleepThread 在那时收到了中断信号。
关键点在于对于 sleepThread.isInterrupted() 调用返回 false。在 SleepRunner 内的 run 方法中,当 Thread.sleep() 因为中断而抛出 InterruptedException 时,JVM 会清除中断状态
这意味着当异常被抛出后,sleepThread 的中断状态会被重置为默认的 false 状态。

comments powered by Disqus