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

Java 入门指南:并发设计模式 —— 两端终止模式

文章目录

    • 基本思想
      • 第一阶段:钩子阶段
        • 实现方法:
      • 第二阶段:屏障阶段
        • 实现方法:
      • 实现两段终止模式的步骤
    • 示例代码
    • 注意事项

Java中的两段终止模式(Two-phase termination pattern),通常称为“钩子-屏障”模式(hook-barrier pattern),是一种用于优雅地安全关闭应用程序的技术。

这种模式旨在确保在应用程序正常关闭时,所有线程都能够正确地进行清理工作,避免资源泄露和其他潜在的问题。尤其是在处理资源密集型任务(如文件I/O、数据库连接等)时尤为重要。

基本思想

两段终止模式通常包括以下几个关键步骤:

  1. 第一阶段(钩子阶段):通知所有线程准备关闭,并执行必要的清理工作。

  2. 第二阶段(屏障阶段):等待所有线程完成清理工作后,最终关闭应用程序。

第一阶段:钩子阶段

在这一阶段,主要目标是通知所有正在运行的线程,应用程序即将关闭,并要求它们开始执行清理操作。这通常通过设置一个共享标志来实现,该标志告诉所有线程应该停止其正常工作并进入清理阶段。

关键步骤

  • 停止接受新的任务或请求。
  • 完成当前正在执行的任务。
  • 通知依赖的系统或服务,表明即将关闭。
  • 开始清理不再需要的资源,如数据库连接、文件句柄等。
实现方法:
  • 共享标志:设置一个全局的布尔变量,表示是否请求了关闭。

  • 中断线程:使用 Thread.interrupt() 来中断线程,这通常用于那些长时间阻塞的线程。

  • 关闭钩子:使用 Runtime.addShutdownHook 添加一个关闭钩子,该钩子会在JVM关闭前被调用。

第二阶段:屏障阶段

在这一阶段,主程序会等待所有线程完成清理工作。如果某些线程未能在指定时间内完成清理,可能需要采取进一步的措施来强制关闭这些线程。

关键步骤

  • 停止所有活动,确保没有任何线程或进程仍在运行。
  • 彻底释放和关闭所有资源。
  • 保存必要的状态信息,以便将来恢复。
  • 执行必要的日志记录,确保关闭过程的可追踪性。
  • 退出应用,返回操作系统或父进程的控制。
实现方法:
  • 等待线程池关闭:使用 ExecutorServiceawaitTermination 方法等待所有任务完成。
  • 超时处理:如果等待超时,则可以使用 shutdownNow 方法立即停止所有线程,并清除未完成的任务。

实现两段终止模式的步骤

  1. 定义状态变量:使用一个或多个状态变量来跟踪应用的关闭状态(如 isShuttingDown)。

  2. 实现准备阶段逻辑:在准备阶段,应用应停止接受新任务,完成当前任务,并清理不必要的资源。这通常涉及对应用架构的深入了解,以确保所有组件都能正确响应关闭信号。

  3. 实现终止阶段逻辑:在终止阶段,应用应停止所有活动,彻底释放资源,并保存必要的状态信息。这可能需要协调多个组件和服务的关闭顺序,以确保没有资源竞争或数据不一致的情况。

  4. 处理异常和错误:在关闭过程中,可能会遇到各种异常和错误。应用应能够优雅地处理这些情况,并记录足够的信息以便进行故障排除。

  5. 测试验证:在开发过程中,应对关闭流程进行彻底的测试,以验证其正确性和健壮性。这包括模拟各种可能的关闭场景和异常情况。

示例代码

下面是一个完整的示例,展示了如何在Java中实现两段终止模式:

public class TwoPhaseTerminationExample {

    private ExecutorService executorService;
    private volatile boolean shutdownRequested = false;

    public TwoPhaseTerminationExample() {
        executorService = Executors.newFixedThreadPool(5); // 创建一个固定大小的线程池
        Runtime.getRuntime().addShutdownHook(new Thread(this::shutdownHook));
    }

    private void shutdownHook() {
        System.out.println("开始关闭钩子...");
        shutdownRequested = true; // 标记为请求关闭
        notifyWorkers(); // 通知所有正在工作的线程
        executorService.shutdown(); // 关闭线程池
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                System.err.println("超时,强制关闭...");
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            System.err.println("等待线程池关闭时被中断...");
            Thread.currentThread().interrupt();
            executorService.shutdownNow();
        }
        System.out.println("关闭钩子执行完毕...");
    }

    private void notifyWorkers() {
        // 通知所有正在工作的线程
        executorService.submit(() -> {
            while (!shutdownRequested) {
                // 工作线程在这里执行任务...
                try {
                    Thread.sleep(1000); // 模拟任务执行
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    System.out.println("任务线程被中断,停止执行...");
                    break;
                }
            }
            System.out.println("任务线程收到关闭信号,开始清理...");
            // 清理操作
        });
    }

    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTerminationExample example = new TwoPhaseTerminationExample();
        Thread.sleep(5000); // 模拟应用程序运行一段时间
        System.out.println("请求关闭应用程序...");
        System.exit(0); // 触发关闭钩子
    }
}
  1. ExecutorService:创建一个固定大小的线程池来模拟多线程环境。

  2. Runtime.addShutdownHook():向 Runtime 添加一个关闭钩子,当JVM准备退出时,这个钩子会被调用。

  3. shutdownHook():关闭钩子的具体实现,设置 shutdownRequested 标志,并通知所有线程进行清理。

  4. notifyWorkers():提交一个任务到线程池,模拟工作线程的行为。当接收到关闭信号时,线程停止工作并进行清理。

注意事项

  1. 中断处理:在多线程环境下,应适当处理线程中断的情况。

  2. 超时处理:在等待线程池关闭时,应设置合理的超时时间,并在超时后采取相应的措施,如强制关闭线程池。

  3. 测试:确保在各种情况下都能正确地关闭应用,并完成所有必要的清理工作。


http://www.kler.cn/a/322401.html

相关文章:

  • More Effective C++ Item 7:区别使用()和{}创建对象
  • 【Pythonr入门第二讲】你好,世界
  • 计算机网络-mac地址与ip地址的区别总结
  • Jmeter 如何导入证书并调用https请求
  • IDEA 开发工具常用快捷键有哪些?
  • K8s 一键部署 MongoDB 的 Replica-Set 和 MongoDB-Express
  • C++之STL—常用排序算法
  • JavaScript 操作 DOM元素CSS 样式的几种方法
  • MATLAB软件开发通用控制的软件架构参考
  • (JAVA)浅尝关于 “栈” 数据结构
  • Android 增加宏开关控制android.bp
  • MySQL查询语句优化
  • DataGrip远程连接Hive
  • Python中列表常用方法
  • C语言 15 预处理
  • vue3 TagInput 实现
  • 监控易监测对象及指标之:Kubernetes(K8s)集群的全方位监控策略
  • webpack与vite读取base64图片
  • django开发流程1
  • manim中实现文字换行和设置字体格式
  • MySQL篇(日志)
  • blender设置背景图怎么添加?blender云渲染选择
  • 学Python再学C++是走弯路?
  • centos7 yum 更新 nginx 到最新版本 1.26
  • 【Kotlin 集合概述】可变参数vararg、中缀函数infix以及解构声明(二十)
  • MySQL record 07 part