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() {@Overridepublic 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终止之前被正确释放。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小红书素人投放计划怎么做?

小红书素人投放是很多品牌在小红书推广打响的第一枪,素人铺量在小红书投放,可以奠定品牌在小红书的声量,小红书素人投放计划怎么做?前期规划好一切,才能在后期让我们的推广爆发出更好的效果。接下来伯乐网络传媒就来给…

【压缩包技巧】如何把rar文件压缩为zip格式?

想要将rar文件压缩为zip格式,其实就是压缩包格式进行转换,今天和大家分享三个rar压缩包改成zip格式的方法,希望能够帮助到大家! 方法一: 直接修改rar压缩包的后缀名变为zip,就可以修改压缩包文件格式了 …

揭秘Google Gemini:AI界的多模态革命者与ChatGPT-4的较量

在人工智能的快速发展浪潮中,Google DeepMind的最新力作——Gemini,以其多模态的超凡能力,正引领着AI技术的新一轮革命。本文将深入探讨Gemini的核心特性、不同版本的特点,以及它与ChatGPT-4的对比优势和差异。 一、Gemini简介 A…

float32 float16 bfloat16 推理训练GPU速度和内存调研

概念: 参考:Accelerating Large Language Models with Mixed-Precision Techniques - Lightning AI 3种数量类型表示的数据范围不一样,以float32为例其中有1个符号位,8位表示指数,23位表示尾数 标准训练推理是用的fl…

eclipse maven 项目导入报错

错误:Internal compiler error: java.lang.NullPointerException at org.eclipse.jdt.internal.compiler.apt.dispatch.AnnotationDiscoveryVisitor 环境:eclipse Kepler Service Release 2 ,JDK1.7 解决办法:编码不对,修改

指令调用模板

也就是这边指令通过id和map会定位到一个结构体,然后这个结构再赋值两个成员,一个是函数一个是指令类型,然后这个函数是模板的实例化 使用的时候就传进去,这只是参数,最开始初始化的时候模板就已经实例化了。然后关于模…

为什么美国硅谷作为服务器托管的首选地?

在数字化时代,服务器托管已成为企业运营不可或缺的一部分。而美国硅谷作为全球科技创新的摇篮,其服务器托管服务备受全球企业青睐。那么,为什么众多企业选择美国硅谷作为服务器托管的首选地呢? 硅谷拥有得天独厚的地理位置和网络基础设施。硅…

[HackMyVM]Quick 2

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

若依框架的使用

文章目录 1,前端2,后端3,数据库4,测试 1,前端 2,后端 3,数据库 4,测试

Vision Transformers的注意力层概念解释和代码实现

2017年推出《Attention is All You Need》以来,transformers 已经成为自然语言处理(NLP)的最新技术。2021年,《An Image is Worth 16x16 Words》,成功地将transformers 用于计算机视觉任务。从那时起,许多基于transformers的计算机…

刘敏:楼氏动铁和麦克风助力听力健康技术发展 | 演讲嘉宾公布

一、助辅听器材Ⅱ专题论坛 助辅听器材Ⅱ专题论坛将于3月28日同期举办! 听力贯穿人的一生,听觉在生命的各个阶段都是至关重要的功能,听力问题一旦出现,会严重影响生活质量。助辅听器材能有效提高生活品质。在这里,我们将…

Redis哨兵模式(Sentinel)的搭建与配置

创建三个Redis实例所需的目录,生产环境需独立部署在不同主机上,提高稳定性。 Redis 哨兵模式(Sentinel)是一个自动监控处理 redis 间故障节点转移工作的一个redis服务端实例,它不提供数据存储服务,只进行普通 redis 节点监控管理,使用redis哨兵模式可以实现redis服务端故…

八、软考-系统架构设计师笔记-系统质量属性和架构评估

1、软件系统质量属性 软件架构的定义 软件架构是指在一定的设计原则基础上,从不同角度对组成系统的各部分进行搭配和安排,形成系统的多个结构而组成架构,它包括该系统的各个构件,构件的外部可见属性及构件之间的相互关系。 软件架…

STM32串口:DMA空闲中断实现接收不定长数据(基于HAL库)

STM32串口:DMA空闲中断实现接收不定长数据(基于HAL库): 第一步:设置rcc,时钟频率,下载方式 设置system core->RCC如图所示:(即High Speed Clock和Low Speed Clock都选…

ansible基础与基础命令模块

一Ansible 1. ansible 的概念 Ansible是一个基于Python开发的配置管理和应用部署工具,现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点,Pubbet和Saltstack能实现的功能,Ansible基本上都可以实现。 Ansible能批量配置、部署、…

手机群控软件开发必备源代码分享!

随着移动互联网的飞速发展,手机群控技术在市场推广、自动化测试、应用管理等领域的应用越来越广泛,手机群控软件作为一种能够同时控制多台手机设备的工具,其开发过程中,源代码的编写显得尤为重要。 1、设备连接与识别模块 设备连…

java Day7 正则表达式|异常

文章目录 1、正则表达式1.1 常用1.2 字符串匹配,提取,分割 2、异常2.1 运行时异常2.2 编译时异常2.3 自定义异常2.3.1 自定义编译时异常2.3.2 自定义运行时异常 1、正则表达式 就是由一些特定的字符组成,完成一个特定的规则 可以用来校验数据…

AHU 汇编 实验二

一、实验名称:实验二 不同寻址方式的灵活运用 二、实验内容:定义数组a[6],用多种寻址方式访问对应元素,实现(a[0]a[1])*(a[2]-a[3])/a[4],将结果保存在内存a[5]中,用debug查询结果。 实验过程&a…

压缩自定义格式压缩包<2>:python使用DEFLATE 算法打包并解压成功,但是解压后的文件格式是固定后缀。

打包 import zlib import osdef compress_folder(input_folder, output_filename):"""使用 DEFLATE 算法压缩文件夹下的所有文件。Parameters:input_folder: str要压缩的文件夹路径。output_filename: str输出压缩文件名。"""# 创建一个空的字节…

GPT与R 在生态环境领域数据统计分析

原文链接:GPT与R 在生态环境领域数据统计分析https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247597092&idx2&sn0a7ac5cf03d37c7b4659f870a7b71a77&chksmfa823dc3cdf5b4d5ee96a928a1b854a44aff222c82b2b7ebb7ca44b27a621edc4c824115babe&…