关闭钩子(shutdown hook)的作用以及在Tomcat中的使用
在很多实际应用环境中,当用户关了应用程序时,需要做一些善后清理工作,但问题是,用户有时并不会按照推荐的方法关闭应用程序,很有可能不做清理工作,例如在Tomcat的部署应用中,通过实例化一个Server对象来启动servlet容器,并调用其start方法,然后逐个调用组件的start方法,正常情况下,为了让Server对象能够关闭这些已经启动的组件,你应该向指定的端口发送关闭命令,如果你只是简单的突然退出,例如在应用程序过程中关闭控制台,可能会发生一些意想不到的事情。
幸运的是,java为程序员提供了一种优雅的方法可以在关闭过程中执行一些代码,这样就能确保那些负责善后处理的代码肯定能够执行,下面将展示如何关闭钩子来确保清理代码总是能够执行,无论用户如何终止程序。
在java中,虚拟机会对两类事件进行响应,然后执行关闭操作,
- 当调用System.exit()方法或者程序的最后一个非守护进程线程退出时,应用程序正常退出
- 用户突然强制虚拟机中断运行,例如用户按CTRL+C快捷键或在为关闭Java程序的情况下,从系统中退出。
虚拟机在执行关闭操作时,会经过以下两个阶段
- 虚拟机启动所有已经注册的关闭钩子,如果有的话,关闭钩子是先前已经通过Runtime类注册的线程,所有的关闭钩子会并发执行,直到完成任务
- 虚拟机根据情况调用所有没有被调用过的终结器(finalizer)
下面重点说明第一个阶段,因为该阶段允许程序员告诉虚拟机在应用程序中执行一些清理代码。关闭钩子 很简单,只是 java.lang.Thread类的一个子类实例,创建关闭钩子也很简单:
- 创建Thread类的一个子类
- 实现你自己的run方法,当应用程序(正常或者突然)关闭时,会调用此方法
- 在应用程序中,实例化 关闭钩子类
- 使用当前Runtime类的addShutdownHook方法注册关闭钩子
也许你已经注意到了,不需要像启动线程一样调用关闭钩子的start方法,虚拟机会在它运行其关闭程序时启动并执行关闭钩子。
下面定义了一个简单的ShutdownHookDemo类和一个Thread类(ShutdownHookDemo类)的子类,,注意,ShutdownHook类的run方法只是简单的将字符串“Shutting down”输出到控制台上,但是可以插入想在应用程序关闭之前的任何代码。
package myex16.pyrmont.shutdownhook; import java.io.IOException; /**
* <p>
* <b>Title:ShutdownHookDemo.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述: 演示 关闭钩子 的简单实用
* </p>
*
* @author 陈东
* @date 2018年12月24日 下午8:01:14
* @version 1.0
*/
public class ShutdownHookDemo { public void start() {
System.out.println("Demo start");
// 创建关闭钩子 就是线程
ShutdownHook shutdownHook = new ShutdownHook();
// 像虚拟机中注册关闭钩子
Runtime.getRuntime().addShutdownHook(shutdownHook);
} /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月24日 下午8:01:15
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
public static void main(String[] args) {
ShutdownHookDemo demo = new ShutdownHookDemo();
demo.start(); try {
// 等待输入 这样 线程就不会走完 然后只要随便输入东西 就会 走完流程 测试在线程运行完之后 虚拟机启动我们注册的关闭钩子 并运行
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } class ShutdownHook extends Thread {
public void run() {
System.out.println("Shutting down");
}
}
运行结果
Demo start
输入了东西
Shutting down
在实例化ShutdownHookDemo类后,main方法会调用start方法,start方法会创建一个关闭钩子,并通过RunTime来注册它:
// 创建关闭钩子 就是线程
28 ShutdownHook shutdownHook = new ShutdownHook();
29 // 像虚拟机中注册关闭钩子
30 Runtime.getRuntime().addShutdownHook(shutdownHook);
然后,应用程序会等待用户输入
System.in.read();
当用户按Enter键时,应用程序退出,但是虚拟机会执行关闭钩子,效果是输出字符串“Shutting down”。
关闭钩子的例子
现在看另一个例子,这是一个简单的Swing应用程序,其类的名字MySwingApp,效果如图

该应用程序会在它启动时创建一个临时文件,并在关闭时删除该临时文件。
package myex16.pyrmont.shutdownhook; import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException; import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea; /**
* <p>
* <b>Title:MySwingApp.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:演示 关闭钩子的使用
* </p>
*
* @author 陈东
* @date 2018年12月24日 下午8:27:54
* @version 1.0
*/
public class MySwingApp extends JFrame { JButton exitButton = new JButton(); JTextArea jTextArea1 = new JTextArea(); String dir = System.getProperty("user.dir");
String filename = "temp.txt"; public MySwingApp() {
exitButton.setText("Exit");
exitButton.setBounds(new Rectangle(304, 248, 76, 37));
exitButton.addActionListener(new java.awt.event.ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
exitButton_actionPerformed(e);
} }); this.getContentPane().setLayout(null);
jTextArea1.setText("Click the Exit button to quit");
jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));
this.getContentPane().add(exitButton, null);
this.getContentPane().add(jTextArea1, null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setBounds(0, 0, 400, 330);
this.setVisible(true);
initialize();
} private void initialize() {
// 创建一个temp.txt文件
File file = new File(dir, filename);
try {
System.out.println("Creating temporary file");
file.createNewFile();
} catch (IOException e) {
System.out.println("Failed creating temporary file.");
}
} /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月24日 下午8:27:54
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
public static void main(String[] args) { MySwingApp mySwingApp = new MySwingApp();
} private void shutdown() {
// 删除这个文件
File file = new File(dir, filename);
if (file.exists()) {
System.out.println("Deleting temporary file.");
file.delete();
}
} void exitButton_actionPerformed(ActionEvent e) {
shutdown();
System.exit(0);
} }
在实例化这个类时,应用程序会调用其initialize方法,然后initialize方法会在用户目录下创建一个临时文件,名为"temp.txt"
private void initialize() {
// 创建一个temp.txt文件
File file = new File(dir, filename);
try {
System.out.println("Creating temporary file");
file.createNewFile();
} catch (IOException e) {
System.out.println("Failed creating temporary file.");
}
}
当用户关闭应用程序时,应用程序需要删除该临时文件,我们希望用户总是能够通过单击Exit按钮来退出,这样就会调用shutdown方法,也就可以删除临时文件了,但是如果用户是通过点击右上角的关闭按钮或者是通过其他方法退出的,临时文件就无法删除了,
下面给的类提供了这个问题的解决方案,使用关闭钩子来删除临时文件,关闭钩子的类是一个内部类,这样它就访问主类的所有方法了,在下面代码中,关闭钩子的run方法会调用shutdown方法,保证在虚拟机关闭时会调用shutdown方法。
package myex16.pyrmont.shutdownhook; import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException; import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea; /**
* <p>
* <b>Title:MySwingApp.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:演示 关闭钩子的使用
* </p>
*
* @author 陈东
* @date 2018年12月24日 下午8:27:54
* @version 1.0
*/
public class MySwingApp extends JFrame { JButton exitButton = new JButton(); JTextArea jTextArea1 = new JTextArea(); String dir = System.getProperty("user.dir");
String filename = "temp.txt"; public MySwingApp() {
exitButton.setText("Exit");
exitButton.setBounds(new Rectangle(304, 248, 76, 37));
exitButton.addActionListener(new java.awt.event.ActionListener() { @Override
public void actionPerformed(ActionEvent e) {
exitButton_actionPerformed(e);
} }); this.getContentPane().setLayout(null);
jTextArea1.setText("Click the Exit button to quit");
jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));
this.getContentPane().add(exitButton, null);
this.getContentPane().add(jTextArea1, null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setBounds(0, 0, 400, 330);
this.setVisible(true);
initialize();
} private void initialize() {
MyShutdownHook hook = new MyShutdownHook();
Runtime.getRuntime().addShutdownHook(hook); // 创建一个temp.txt文件
File file = new File(dir, filename);
try {
System.out.println("Creating temporary file");
file.createNewFile();
} catch (IOException e) {
System.out.println("Failed creating temporary file.");
}
} /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月24日 下午8:27:54
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
@SuppressWarnings("unused")
public static void main(String[] args) { MySwingApp mySwingApp = new MySwingApp();
} private void shutdown() {
// 删除这个文件
File file = new File(dir, filename);
if (file.exists()) {
System.out.println("Deleting temporary file.");
file.delete();
}
} void exitButton_actionPerformed(ActionEvent e) {
shutdown();
System.exit(0);
} @SuppressWarnings("unused")
private class MyShutdownHook extends Thread {
public void run() {
shutdown();
}
} }
注意 这次的initialize方法,他首先会创建内部类MyShutdownHook的一个实例,该类继承自java.lang.Thread类
MyShutdownHook hook = new MyShutdownHook();
一旦获得了MyShutdownHook类的实例后。就需要将其值传给Rutime类的addShutDownhook方法
Runtime.getRuntime().addShutdownHook(hook);
initialize方法剩余代码就与上一个示例类似了 创建临时文件,
现在启动上面代码,检查一下,当突然关闭应用程序时,是否总是删除临时文件。
注意:关闭钩子 的run方法总会执行,
将上面例子中的关闭钩子的run方法替换一下
@SuppressWarnings("unused")
private class MyShutdownHook extends Thread {
public void run() {
System.out.println("关闭钩子执行了");
shutdown();
}
}
然后在执行示例,在通过点击按钮正常退出时输出如下
Creating temporary file
Deleting temporary file.
关闭钩子执行了
通过点击 右上角的 X 关闭输出如下
Creating temporary file
关闭钩子执行了
Deleting temporary file.
注意一下 钩子run执行的 顺序
- 第一种情况:正常关闭时 是在 执行完shutdown方法之后 虚拟机执行的 关闭钩子
- 第二种: 非正常时,是在发生被点击X 之后, 虚拟机执行的关闭钩子
只要有关闭钩子 那么除非进行注销 否则一定会被执行
Runtime.getRuntime().removeShutdownHook(shutdownHook);
从Rutime中移除 关闭钩子,
Tomcat中的关闭钩子
那么重点来了 既然在Tomcat学习中将这个肯定是 ,Tomcat也用到了关闭钩子来完成退出过程的,在 org,apache.catalina.startup.Catalina类中,可以找到这样的代码,Catalina类负责启动管理其他组件的Srver对象。一个名为CatalinaShutdownHook的内部类继承自Thread类,提供了run方法的实现,它调用server对象的stop方法,执行关闭操作,
/**
*
* 关闭钩,这将执行清洁关闭 Catalina
*/
protected class CatalinaShutdownHook extends Thread { public void run() { if (server != null) {
try {
((Lifecycle) server).stop();
} catch (LifecycleException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
} }
}
在Catalina实例启动时,会实例化关闭钩子,并在一个阶段将其添加到Rutime类中,
有时候,应用程序在关闭之前应该执行一些代码清理工作,但是你不能价设定用户总是正常退出,那么这次介绍的关闭钩子提供了一种解决方案,确保无论用户如何关闭应用程序,清理代码总是能得到执行。
关闭钩子(shutdown hook)的作用以及在Tomcat中的使用的更多相关文章
- 关闭钩子(shutdown hook)的作用
DK1.3介绍了java.lang.Runtime class的addShutdownHook()方法.如果你需要在你的程序关闭前采取什么措施,那么关闭钩子(shutdown hook)是很有用的. ...
- JAVA虚拟机关闭钩子(Shutdown Hook)
程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码.JAVA中的ShutdownHook提供了比较好的方案. JDK提供了Java.Run ...
- Java关闭钩子的应用 - Shutdown Hook
背景 在开发中,遇到这种情况,多个线程同时工作,突然一个线程遇到了fetal的错误,需要立即终止程序,等人工排查解决了问题之后重新启动.但是这样会有一个问题,程序终止时,其他线程可能正在进行重要操作, ...
- tomcat关闭钩子
转 http://501565246-qq-com.iteye.com/blog/1733575 21,tomcat关闭钩子 博客分类: tomcat 在很多环境下,在关闭应用程序的时候需要做一些 ...
- java的关闭钩子(Shutdown Hook)
Runtime.getRuntime().addShutdownHook(shutdownHook); 这个方法的含义说明: 这个方法的意思就是在jvm中增加一个关闭的钩子,当jv ...
- Spark学习:ShutdownHookManager虚拟机关闭钩子管理器
Java程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码. JAVA中的ShutdownHook提供了比较好的方案. JDK提供了Jav ...
- Java Shutdown Hook 场景使用和源码分析
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 背景 ...
- JVM 关闭钩子
1.功能 在jvm中添加关闭钩子(Runtime.getRuntime().addShutdownHook(shutdownHook);)后,当jvm关闭时会执行系统中已经设置的所有通过该方法添加的钩 ...
- 【Hook技术】实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展
[Hook技术]实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展 公司有个监控程序涉及到进程的保护问题,需要避免用户通过任务管理器结束掉监控进程,这里使用 ...
随机推荐
- MySQL优化相关参数--先做个记录,以后可能用得到
innodb_io_capacity:可设置的磁盘IO性能参数,越高代表当前mysql的IO性能更好,可用做决策刷脏页速度的参数: innodb_flush_neighbors:刷脏页是否开启连坐机制 ...
- 计数原理,递推,求从左边能看到l个棒子,右边能看到r个棒子的方案数目
题意 有高为 1, 2, …, n 的 n 根杆子排成一排, 从左向右能看到 L 根, 从右向左能看到 R 根.求有多少种可能的排列方式. solution: 数据范围仅200,本来是往组合数学方 ...
- Python 文件writelines() 方法和处理双层列表
概述 writelines() 方法用于向文件中写入一序列的字符串. 这一序列字符串可以是由迭代对象产生的,如一个字符串列表. 换行需要制定换行符 \n. 语法 writelines() 方法语法如下 ...
- R语言:各类型数据文件的导入
导入csv: read.csv() 导入txt: read.table() 注意,txt文件编码为unicode的导入r会报错,需转换成ANSI 读入excel:需要安装xlsx包,安装此包前先下载好 ...
- 前端知识点回顾——Javascript篇(一)
DOM特殊元素获取 document.documentElement //HTML标签 document.head //head标签 document.title //title标签 document ...
- UML 2.5版本与UML分类概述
UML 2.5版本与UML分类概述 转 http://www.umlstudy.com/uml-25-diagrams.html UML简述 UML图是设计.实现或已经存在的系统模型的部分图形表示(视 ...
- selenium 学习中遇到的问题汇总
1.使用document.getByClassName时无click事件,然后就不知道怎么办了,也不太懂前端,与开发大哥确认,div 中class实现展开和收起是通过隐藏和显示这种方式实现的,在编写时 ...
- [Java读书笔记] Effective Java(Third Edition) 第 3 章 对于所有对象都通用的方法
第 10 条:覆盖equals时请遵守通用约定 在不覆盖equals方法下,类的每个实例都只与它自身相等. 类的每个实例本质上都是唯一的. 类不需要提供一个”逻辑相等(logical equality ...
- CreationPolicy 枚举的值
根据[CreationPolicy]枚举的值,我们很容易就能看出其代表的意义,[Shared]代表共享部件,即单例,所有的导入都使用一个实例,如果组合引擎中没有该实例,则会创建,一旦有了,就不会再创建 ...
- c++ STL之map
map内部自建一颗红黑树(一 种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,map中的元素是自动按Key升序排序,所以不能对map用sort函数: ...