如何正确停止一个线程
# 禁止使用的 api
- stop(): 强制线程终止,容易造成数据不一致问题
- suspend(): 此方法已被弃用,因为它本身就容易出现死锁。
- resume(): 此方法仅适用于suspend() ,由于它易于死锁,因此已被弃用。
# 正确停止线程 - 两阶段终止模式
- 外部线程向线程发起终止指令
- 内部线程接收到终端指令,自行处理安全停止逻辑
由于 JVM 的异常处理会清除线程的中断状态,在处理中断逻辑处理完成时,通常需要重新设置线程中断状态
另外,在 run 方法中,我们可能会调用第三方类库,如果第三方类库捕获到 InterruptedException 异常后没有重新设置线程的中断状态,那么就会导致线程不能够正常终止,所以通常会使用自定义终止标志变量来判断是否需要停止线程
例如这段代码可能出现死循环
Thread th = Thread.currentThread();
while(true) {
if(th.isInterrupted()) {
break;
}
// 省略业务代码无数
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
// Thread.currentThread().interrupt();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
以生产者消费者模式举例
提示
线程中断标志位变量需要使用 volatile
关键字修饰,保证线程可见性
参考示例代码 Producer.terminated
变量
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class ThreadStopDemo {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(50);
Producer p = new Producer(queue);
Consumer c = new Consumer(queue, p);
p.start();
c.start();
try {
p.join();
c.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
@RequiredArgsConstructor
class Producer extends Thread {
private final BlockingQueue<Integer> queue;
volatile boolean terminated = false;
@Override
public void run() {
int num = 0;
while (!terminated) {
try {
queue.put(num++);
System.out.println("put " + num);
} catch (InterruptedException e) {
System.out.println("put interrupt");
// 阶段2:接收到中断指令,自行处理逻辑,保证逻辑完整性,并重新设置线程中断状态
Thread.currentThread().interrupt();
}
}
System.out.println("生产结束");
}
/**
* 退出线程
*/
public void quit() {
System.out.println("生产停止");
terminated = true;
// 阶段1,向线程发起中断指令
this.interrupt();
}
}
@RequiredArgsConstructor
class Consumer extends Thread {
private final BlockingQueue<Integer> queue;
private final Producer producer;
List<Integer> list = new ArrayList<>(10);
@Override
public void run() {
while (list.size() < 100) {
try {
Integer num = queue.take();
list.add(num);
System.out.println("take " + num);
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("消费结束");
producer.quit();
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
输出
put 1
put 2
put 3
put 4
take 0
take 1
put 5
take 2
put 6
take 3
put 7
take 4
put 8
消费结束
生产停止
put interrupt
生产结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Last Updated: 2024/04/23, 01:30:37