当前位置: 首页 > news >正文

多线程之两阶段终止模式

两阶段终止模式


当某些线程正在运行时,如何去正确终止线程,如果直接强制终止线程(如调用 Thread.stop())会带来以下问题:

  • 资源未释放:线程可能持有锁、打开文件或网络连接未关闭。
  • 数据不一致:线程可能正在修改共享数据,突然终止会导致数据损坏。
  • 不可控性:无法保证线程在安全点终止。

两阶段终止模式(Two-Phase Termination Pattern)是一种多线程编程中安全终止线程的设计模式。它的核心思想是:在终止线程前,先发出终止请求(第一阶段),然后线程在完成必要清理工作后再真正终止(第二阶段)。这种模式避免了直接强制终止线程导致的资源泄露、数据不一致等问题

简单来说就是在一个线程T1中如何优雅的终止线程T2,给T2一个料理后事的机会


两阶段终止的实现步骤

第一阶段:发出终止请求

  • 设置一个终止标志位,通知线程需要终止。
  • 如果线程处于阻塞状态(如 sleep()wait()),需要通过**中断(Interrupt)**唤醒它。

第二阶段:线程响应终止请求

  1. 线程检测到终止标志位或中断信号后,停止接受新任务
  2. 执行清理工作(如释放锁、关闭文件)。
  3. 安全终止线程

如果你依然认为难以理解,可以用狼人杀的角度来解释一下:

使用Thread.stop()等强制终止线程,就是夜间被狼刀了,而两阶段终止模式是白天被票决,你还有发表遗言的机会


代码实现

这里启用了一个监控线程,在监控线程中用了一个死循环,并且用线程的中断状态作为标志位,如果线程在运行时被打断可直接进入第二阶段,若是线程在睡眠中被打断,在捕获InterruptedException后,重新设置中断状态,以便标志位终止线程

public class TwoPhase {
    /**
     * 两阶段终止模式:
     *  在一个线程T1中如何优雅的终止线程T2,给T2一个料理后事的机会
     */
    private Thread monitor;

    // 启动监控线程
    public void start(){
        monitor = new Thread(() -> {
            while (true){
                Thread current = Thread.currentThread();
                if (current.isInterrupted()){ // 判断线程中断状态
                    System.err.println("线程被打断,料理后事中.............");
                    break;
                }
                try {
                    Thread.sleep(2000);
                    System.out.println("记录线程日志--------------------");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.err.println("------------->重置线程打断标识");
                    current.interrupt();
                }
            }
            System.out.println("--------------线程已终止-----------------");
        });
        monitor.start();
    }
    // 终止监控线程
    public void stop(){
        monitor.interrupt();
    }
}

此时可以创建一个测试类,启动监控线程,等待几秒之后用stop终止该线程

public class TwoPhaseTest {
    public static void main(String[] args) throws InterruptedException {
        TwoPhase twoPhase = new TwoPhase();
        twoPhase.start();
        Thread.sleep(7000);
        twoPhase.stop();
    }
}

控制台会依次输出信息:

记录线程日志--------------------
记录线程日志--------------------
记录线程日志--------------------
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at juc2.TwoPhase.lambda$start$0(TwoPhase.java:20)
	at java.base/java.lang.Thread.run(Thread.java:834)
------------->重置线程打断标识
线程被打断,料理后事中.............
--------------线程已终止-----------------

大致流程图:

✅ 是
❎ 否
🚫 无异常
⚠️ 有异常
🔁 while true
❓ 是否被打断?
🛠️ 料理后事
⏹️ 结束循环
💤 sleep(2000)
📝 执行监控记录
⚡ 设置打断标记

相关文章:

  • 【DeepSeek】本地部署,保姆级教程
  • scala中为什么能用常量的地方就不用变量
  • Miniconda + VSCode 的Python环境搭建
  • 解锁观察者模式:Java编程中的高效事件管理之道
  • 【Windows软件 - HeidiSQL】导出数据库
  • golang常用库之-swaggo/swag根据注释生成接口文档
  • halcon 条形码、二维码识别、opencv识别
  • Java零基础入门笔记:(4)方法
  • 每日一题——将数字字符串转化为IP地址
  • 深入解析 iOS 视频录制(三):完整录制流程的实现与整合
  • 如何连接别人的redis服务器吗?
  • 嵌入式八股文(四)计算机网络篇
  • selenium环境搭建
  • 【故障处理】- 11G expdp导出缓慢 + Streams AQ: enqueue blocked on low memory等待事件
  • mybatis 批量提交-提升效率
  • P11071 「QMSOI R1」 Distorted Fate Solution
  • C# 使用 CSRedis 来操作 Redis 队列
  • ART光学跟踪系统在汽车制造与设计审核中的实际应用
  • 使用IDEA创建Maven项目、Maven坐标,以及导入Maven项目
  • Java的反射
  • 山东14家城商行中,仅剩枣庄银行年营业收入不足10亿
  • 中华人民共和国和俄罗斯联邦关于全球战略稳定的联合声明
  • 洞天寻隐·学林纪丨玉洞桃源:仇英青绿山水画中的洞天与身体
  • 澎湃研究所“营商环境研究伙伴计划”启动
  • 胖东来发布和田玉、翡翠退货说明:不扣手续费等任何费用
  • 公募基金改革八大要点:建立浮动管理费收取机制、降低规模排名考核权重