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 状态。