Java - 探究Java优雅退出的两种机制

文章目录

  • 概述
  • Java优雅停机_ ShutdownHook 机制
    • 步骤
    • Code
  • Java优雅停机_ 信号量机制
    • SignalHandler 工作原理
    • 使用步骤
    • Linux支持的信号量
    • 根据操作系统选择信号量
    • Code
  • 注意事项

在这里插入图片描述

在这里插入图片描述


概述

在Linux上通过kill -9 pid方式强制终止进程的副作用,这种方式虽然简单高效,但也会带来一些问题,特别是对于应用软件而言。这些问题包括但不限于:

  1. 数据丢失:由于缓存中的数据尚未持久化到磁盘中,强制终止进程可能导致数据丢失,尤其是对于数据库等需要持久化数据的应用。

  2. 文件损坏:如果进程正在进行文件写操作,但尚未完成,突然退出可能导致文件损坏,进而影响数据完整性。

  3. 请求丢失:如果线程的消息队列中有未处理的请求消息,强制终止进程可能导致请求消息丢失,进而影响系统的正常运行。

  4. 应答消息未返回:如果数据库操作已经完成,但应答消息尚未返回给客户端,强制终止进程可能导致客户端等待超时,带来重复更新等问题。

  5. 句柄资源未释放:强制终止进程可能导致句柄资源没有及时释放,从而影响系统的性能和稳定性。

综上所述,虽然强制终止进程是一种简单高效的方式,但在实际应用中需要谨慎使用,尤其是对于需要保证数据完整性和系统稳定性的应用场景,建议使用更加安全可靠的方式来终止进程,比如通过正常的关闭流程来释放资源和保证数据一致性。


Java优雅停机_ ShutdownHook 机制

Java的优雅停机通常通过注册JDK的ShutdownHook来实现,当系统接收到退出指令时,首先标记系统处于退出状态,不再接收新的消息,然后将积压的消息处理完,最后调用资源回收接口将资源销毁,各线程退出执行。

Java的ShutdownHook(关闭钩子)是一种机制,允许开发人员在Java虚拟机(JVM)即将关闭时执行一些特定的代码。

这些代码通常用于释放资源、保存状态或执行清理操作,以确保应用程序在退出时能够完成一些必要的步骤。

ShutdownHook提供了一种优雅退出的机制,使得应用程序可以在正常关闭时执行一些清理工作,而不会因为突然的中断而丢失数据或状态。

步骤

  1. 注册ShutdownHook: 在Java中,可以通过Runtime类的addShutdownHook(Thread hook)方法来注册ShutdownHook。注册的ShutdownHook是一个线程对象,当JVM即将关闭时,会依次执行这些线程对象的代码。

  2. 执行时机: ShutdownHook在以下情况下会被执行:

    • 程序正常退出(调用System.exit(int status)方法)
    • 用户按下Ctrl+C终止程序
    • 操作系统关闭
    • JVM崩溃
  3. 执行顺序: 注册的ShutdownHook会按照注册的顺序依次执行。但是不能保证所有的ShutdownHook都会被执行,因为在某些情况下,比如JVM崩溃,kill -9可能无法正常执行ShutdownHook

  4. 注意事项:

    • ShutdownHook应该尽量保持简单和快速执行,不应该执行过多的操作或者占用太多的时间。
    • 不建议在ShutdownHook中执行一些需要等待的操作,比如等待网络连接、等待I/O操作完成等,因为在JVM关闭时时间是有限的,不能保证这些操作能够正常完成。

Code

package com.artisan.nettycase.a01exist;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class ShutdownHookExample {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("ShutdownHook executed.");
            // 在这里执行清理操作或者释放资源的代码
        }));

        // 程序正常执行
        System.out.println("Program is running...");

        // 模拟程序执行
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.exit(-1);
    }
}
    

当程序执行到System.exit(int status)或者不写System.exit(int status)手工按下Ctrl+C终止程序时,注册的ShutdownHook会被执行,输出"ShutdownHook executed."的信息。

在这里插入图片描述


Java优雅停机_ 信号量机制

除了注册ShutdownHook,还可以通过监听信号量并注册SignalHandler 的方式实现优雅退出。

这种方式通常用于在Unix/Linux系统上,通过捕获特定的信号来执行一些清理操作。Java提供了sun.misc.Signalsun.misc.SignalHandler类来支持这种方式。

SignalHandler 工作原理

SignalHandler的工作原理是通过Java的本地方法接口(JNI)与底层操作系统交互来实现的。具体来说,SignalHandler在Java中是一个接口,它由sun.misc包下的Java类提供。当注册了SignalHandler之后,Java程序会通过JNI调用底层操作系统提供的信号处理函数,将Java程序的信号处理器与操作系统的信号处理机制关联起来。

具体步骤如下:

  1. 注册SignalHandler: Java程序调用sun.misc.Signal.handle(Signal signal, SignalHandler handler)方法注册SignalHandler,将信号对象和信号处理器对象关联起来。

  2. JNI调用: JVM通过JNI调用底层操作系统提供的信号处理函数,将Java程序中注册的SignalHandler传递给操作系统。

  3. 信号处理函数: 底层操作系统收到相应的信号(比如Ctrl+C中断信号),会调用注册的信号处理函数。

  4. 调用Java程序中的信号处理器: 信号处理函数在收到信号后会调用Java程序中注册的信号处理器,执行相应的处理逻辑。

  5. 执行清理操作: Java程序中的信号处理器执行相应的清理操作或释放资源的代码。

总的来说,SignalHandler的工作原理是通过JNI接口与底层操作系统交互,将Java程序中的信号处理器与操作系统的信号处理机制连接起来,实现了对特定信号的监听和处理。


使用步骤

通过监听信号量并注册SignalHandler的方式实现优雅退出的步骤如下:

  1. 创建SignalHandler对象: 首先,需要创建一个实现了sun.misc.SignalHandler接口的信号处理器对象。这个对象将负责处理接收到的信号。

  2. 实现handle方法: 在SignalHandler对象中实现handle(Signal signal)方法,该方法定义了接收到信号时需要执行的操作。在这个方法中,你可以执行一些清理操作、释放资源等。

  3. 创建Signal对象: 使用sun.misc.Signal类创建信号对象,指定要监听的信号名称。

  4. 注册SignalHandler: 调用sun.misc.Signalhandle(Signal signal, SignalHandler handler)方法注册信号处理器,将信号对象和信号处理器对象关联起来。

  5. 监听信号: 通过监听信号对象,等待接收信号。


Linux支持的信号量

信号名称作用
SIGKILL终止进程,强制杀死进程
SIGTERM终止进程,软件终止信号
SIGTSTP停止进程,终端来的停止信号
SIGUSR1终止进程,用户定义信号1
SIGUSR2终止进程,用户定义信号2
SIGINT终止进程,中断进程
SIGQUIT建立core文件终止进程,并且生成core文件

根据操作系统选择信号量

String signalName = System.getProperties().getProperty("os.name")
        .toLowerCase().startsWith("win") ? "INT" : "TERM";

// 使用signalName来处理信号
if (signalName.equals("INT")) {
    // Windows操作系统,选择SIGINT信号
    // 执行SIGINT信号的处理逻辑
} else {
    // 非Windows操作系统,选择SIGTERM信号
    // 执行SIGTERM信号的处理逻辑
}

根据这段代码,如果是Windows操作系统,则选择SIGINT信号,用于接收Ctrl+C中断的指令;如果不是Windows操作系统,则选择SIGTERM信号,用于接收kill pid指令。



Code

演示如何通过监听信号量并注册SignalHandler实现优雅退出 。

 package com.artisan.nettycase.a01exist;

import sun.misc.Signal;
import sun.misc.SignalHandler;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */

public class SignalHandlerExample {
    public static void main(String[] args) {
        SignalHandler handler = new SignalHandler() {
            @Override
            public void handle(Signal signal) {
                System.out.println("Received signal: " + signal.getName());
                // 在这里执行清理操作或者释放资源的代码
                // 正常退出程序
                System.exit(0); 
            }
        };

        String sig = System.getProperties().getProperty("os.name").toLowerCase().startsWith("win") ? "INT" : "TERM";

        // 注册SIGINT信号处理器
        Signal.handle(new Signal(sig), handler);


        // 模拟程序执行
        while (true) {
            // 在这里执行业务逻辑
            try {
                Thread.sleep(1000); // 模拟程序执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上述示例中,创建了一个SignalHandler对象来处理SIGINT或者SIGTERM信号,当接收到这些信号时,会执行handle(Signal signal)方法中的清理操作,并正常退出程序。通过调用Signal.handle(Signal signal, SignalHandler handler)方法来注册信号处理器。

在这里插入图片描述


注意事项

使用Java的ShutdownHook(注册在JVM层面的钩子)进行应用的优雅退出时,有几个重要的注意事项:

  1. ShutdownHook的执行不确定性:ShutdownHook并非在所有情况下都会被JVM执行。例如,如果JVM因为某种错误而崩溃,或者接收到某些信号量(如SIGKILL),又或者尝试去杀掉一个不存在的进程(kill -9 pid),ShutdownHook可能不会被执行。因此,不应依赖ShutdownHook来确保某些资源的绝对释放,特别是对于那些对系统稳定性至关重要的资源。

  2. ShutdownHook的执行顺序:JVM不保证ShutdownHook的执行顺序,如果在一个应用中注册了多个ShutdownHook,它们可能不会按照添加的顺序执行,因此不应该在ShutdownHook中依赖于这个顺序来处理资源。

  3. 动态添加或移除ShutdownHook的限制:在JVM关闭期间,不能再动态地添加或移除ShutdownHook。因此,所有的Hook必须在JVM启动时就已经设置好。

  4. 避免在ShutdownHook中调用System.exit():如果在ShutdownHook中调用System.exit(),会导致当前的JVM进程卡住,无法正常退出。这是因为System.exit()会触发终结器(Terminator)进程,这是一个操作系统级别的操作,它会等待当前进程中的所有线程都结束之后,才会释放资源并退出进程。因此,如果在ShutdownHook中调用System.exit(),会导致资源无法正确释放,从而可能引发资源泄漏。

  5. 对于采用注册SignalHandler实现优雅退出的程序,在handle接口中一定要避免阻塞操作,否则它会导致已经注册的ShutdownHook无法执行,系统也无法退出 。

综上所述,ShutdownHook是一个很有用的特性,但是使用时需要谨慎,主要是为了确保资源的正确释放和应用程序的优雅退出。不过,对于那些特别关键的资源,最好还是有其他更可靠的机制来确保它们在JVM终止之前被正确释放。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/445876.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用Windows API实现一个简单的串口助手

使用Windows API实现一个简单的串口助手 目录 使用window API开发一个具有字符串收发功能的串口助手 开发环境串口设备相关的API步骤实现代码收发测试图 使用window API开发一个具有字符串收发功能的串口助手 开发环境 Visual Studio 2015 串口设备相关的API CreateFile 参…

【MySQL | 第四篇】区分SQL语句的书写和执行顺序

文章目录 4.区分SQL语句的书写和执行顺序4.1书写顺序4.2执行顺序4.3总结4.4扩充&#xff1a;辨别having与where的异同&#xff1f;4.5聚合查询 4.区分SQL语句的书写和执行顺序 注意&#xff1a;SQL 语句的书写顺序与执行顺序不是一致的 4.1书写顺序 SELECT <字段名> …

Nwatch在stm32上的移植

目录 Nwatch在stm32上的移植前言实验目的移植game1_task任务相关代码片段结果本文中使用的工程 Nwatch在stm32上的移植 本文目标&#xff1a;Nwatch在stm32上的移植 按照本文的描述&#xff0c;应该可以跑通实验并举一反三。 先决条件&#xff1a;装有编译和集成的开发环境&…

不允许你不知道Python作用域

在Python中&#xff0c;变量的作用域限制非常重要。根据作用域分类&#xff0c;有局部、全局、函数和内建作用域。无作用域限制的变量可以在分支语句和循环中定义&#xff0c;并在外部直接访问。不同的作用域决定了变量的可访问范围&#xff0c;访问权限取决于变量的位置。 1.…

力扣中档题:旋转链表

思路&#xff1a;将链表数据放到数组中&#xff0c;将数组旋转&#xff0c;然后再赋值给链表 struct ListNode* rotateRight(struct ListNode* head, int k) {if(headNULL){return NULL;}int count0;struct ListNode*goodhead;while(good){count;goodgood->next;}int round…

解锁网络数据:入门级IP代理使用教程

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

10 vector的使用

文档介绍 文档介绍 1.vector是表示可变大小数组的容器 2.就像数组一样&#xff0c;vecotr也采用的连续存储空间来存储元素&#xff0c;也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;大小是可以动态改变的&#xff…

【Web】浅聊Java反序列化之C3P0——URLClassLoader利用

目录 前言 C3P0介绍 回归本源——序列化的条件 利用链 利用链分析 入口——PoolBackedDataSourceBase#readObject 拨云见日——PoolBackedDataSourceBase#writeObject 综合分析 EXP 前言 这条链最让我眼前一亮的就是对Serializable接口的有无进行了一个玩&#xff0c…

权限管理系统-0.2.0

三、菜单管理接口 3.1 创建SysMenu类及相关类 首先创建sys_menu表&#xff1a; CREATE TABLE sys_menu (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 编号,parent_id bigint(20) NOT NULL DEFAULT 0 COMMENT 所属上级,name varchar(20) NOT NULL DEFAULT COMMENT 名称,…

【脚本玩漆黑的魅影】寂雨镇全自动练级

文章目录 原理全部代码 原理 老样子。 治疗路径&#xff0c;练级路径。 def zhi_liao(): # 去治疗walk(RIGHT)walk(RIGHT)press(UP, 0.4)for i in [1, 2, 3, 4]:press(A)for i in [1, 2, 3, 4]:press(B)press(DOWN, 0.4)press(LEFT) def chu_qu(): # 右逛c.press(B)press(…

力扣同类题:重排链表

很明显做过一次 class Solution { public:void reorderList(ListNode* head) {if(!head||!head->next)return;ListNode *fasthead,*lowhead;ListNode *prenullptr,*curnullptr,*nextnullptr;while(fast->next!nullptr){fastfast->next;if(fast->next)fastfast->…

风控规则决策树可视化(升级版)

上一篇我们介绍了如何通过交叉表来生成规则&#xff0c;本篇我们来介绍一种可以生成多规则的方法&#xff0c;决策树。除了做模型以外&#xff0c;也可以用来挖掘规则&#xff0c;原理是一样的。 下面通过sklearn的决策树方法来实现风控规则的发现&#xff0c;同时分享一种可以…

【联邦学习综述:概念、技术】

出自——联邦学习综述&#xff1a;概念、技术、应用与挑战。梁天恺 1*&#xff0c;曾 碧 2&#xff0c;陈 光 1 从两个方面保护隐私数据 硬件层面 可 信 执 行 环 境 &#xff08;Trusted Execution Environment&#xff0c;TEE&#xff09;边 缘 计 算&#xff08;Edge Com…

电动车窗开关中MOS管的应用解析

随着科技的不断发展&#xff0c;电动车窗系统已经成为现代汽车中不可或缺的一部分。而MOS&#xff08;金属氧化物半导体&#xff09;管的应用&#xff0c;为电动车窗开关注入了新的活力&#xff0c;极大地提高了其使用寿命和安全性。 一、MOS的优越性能 MOS管以其卓越的开关…

磁盘无法访问?别慌,这里有解决之道!

电脑中&#xff0c;那块储存着重要文件与数据的磁盘&#xff0c;突然之间无法访问&#xff0c;是不是让你感到惊慌失措&#xff1f;面对这样的突发状况&#xff0c;很多人可能会感到手足无措。但别担心&#xff0c;本文将为你解析磁盘无法访问的原因&#xff0c;并提供实用的数…

小文件问题及GlusterFS的瓶颈

01海量小文件存储的挑战 为了解决海量小文件的存储问题&#xff0c;必须采用分布式存储&#xff0c;目前分布式存储主要采用两种架构&#xff1a;集中式元数据管理架构和去中心化架构。 (1)集中式元数据架构&#xff1a; 典型的集中式元数据架构的分布式存储有GFS&#xff0…

代码讲解:如何把3D数据转换成旋转的视频?

目录 3D数据集下载 读取binvox文件 使用matplotlib创建图 动画效果 完整代码 3D数据集下载 这里以shapenet数据集为例&#xff0c;可以访问外网的可以去直接申请下载&#xff1b;我也准备了一个备份在百度网盘的数据集&#xff0c;可以参考&#xff1a; ShapeNet简介和下…

Leetcode 54. 螺旋矩阵

题目描述&#xff1a; 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 示例 2&#xff1a; 输入&a…

Linux文件系列: 深入理解缓冲区和C标准库的简单模拟实现

Linux文件系列: 深入理解缓冲区和C标准库的简易模拟实现 一.缓冲区的概念和作用二.一个样例三.理解样例1.样例解释2.什么是刷新? 四.简易模拟实现C标准库1.我们要实现的大致框架2.mylib.h的实现1.文件结构体的定义2.myfopen等等函数的声明3.完整mylib.h代码 3.myfopen函数的实…

备战蓝桥杯Day25 - 二叉搜索树

一、基本概念 二叉搜索树&#xff08;Binary Search Tree&#xff09;&#xff0c;又称为二叉查找树或二叉排序树&#xff0c;是一种具有特定性质的二叉树。 定义&#xff1a;二叉搜索树可以是一棵空树&#xff0c;也可以是具有以下特性的非空二叉树&#xff1a; 若其左子树不…