铁匠 铁匠
首页
收藏
java
架构之路
常用算法
  • Java
  • nginx
  • 系统运维
  • 系统安全
  • mysql
  • redis
参考文档
关于
链接
  • 分类
  • 标签
  • 归档

专注、不予评判地关注当下
首页
收藏
java
架构之路
常用算法
  • Java
  • nginx
  • 系统运维
  • 系统安全
  • mysql
  • redis
参考文档
关于
链接
  • 分类
  • 标签
  • 归档
  • java api 文档
  • 集合

  • 版本特性

  • jvm

  • 网络编程

  • 并发编程

    • 并发编程三大特性
    • 线程的基本概念
    • 如何正确停止一个线程
      • 禁止使用的 api
      • 正确停止线程 - 两阶段终止模式
    • java线程池
    • 线程池大小设置多少合适
    • java 常用队列
    • 使用 Semaphore 实现一个简单限流器
  • java
  • 并发编程
FengJianxin
2022-05-11
目录

如何正确停止一个线程

# 禁止使用的 api

  • stop(): 强制线程终止,容易造成数据不一致问题
  • suspend(): 此方法已被弃用,因为它本身就容易出现死锁。
  • resume(): 此方法仅适用于suspend() ,由于它易于死锁,因此已被弃用。

# 正确停止线程 - 两阶段终止模式

  1. 外部线程向线程发起终止指令
  2. 内部线程接收到终端指令,自行处理安全停止逻辑

由于 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

以生产者消费者模式举例

提示

线程中断标志位变量需要使用 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

输出

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
#并发编程
线程的基本概念
java线程池

← 线程的基本概念 java线程池→

最近更新
01
策略模式
01-09
02
模板方法
01-06
03
观察者模式
01-06
更多文章>
Theme by Vdoing | Copyright © 2016-2023 铁匠 | 粤ICP备15021633号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式