在很多实际应用环境中,当用户关了应用程序时,需要做一些善后清理工作,但问题是,用户有时并不会按照推荐的方法关闭应用程序,很有可能不做清理工作,例如在Tomcat的部署应用中,通过实例化一个Server对象来启动servlet容器,并调用其start方法,然后逐个调用组件的start方法,正常情况下,为了让Server对象能够关闭这些已经启动的组件,你应该向指定的端口发送关闭命令,如果你只是简单的突然退出,例如在应用程序过程中关闭控制台,可能会发生一些意想不到的事情。

  幸运的是,java为程序员提供了一种优雅的方法可以在关闭过程中执行一些代码,这样就能确保那些负责善后处理的代码肯定能够执行,下面将展示如何关闭钩子来确保清理代码总是能够执行,无论用户如何终止程序。

  在java中,虚拟机会对两类事件进行响应,然后执行关闭操作,

  • 当调用System.exit()方法或者程序的最后一个非守护进程线程退出时,应用程序正常退出
  • 用户突然强制虚拟机中断运行,例如用户按CTRL+C快捷键或在为关闭Java程序的情况下,从系统中退出。

虚拟机在执行关闭操作时,会经过以下两个阶段

  1. 虚拟机启动所有已经注册的关闭钩子,如果有的话,关闭钩子是先前已经通过Runtime类注册的线程,所有的关闭钩子会并发执行,直到完成任务
  2. 虚拟机根据情况调用所有没有被调用过的终结器(finalizer)

  下面重点说明第一个阶段,因为该阶段允许程序员告诉虚拟机在应用程序中执行一些清理代码。关闭钩子 很简单,只是 java.lang.Thread类的一个子类实例,创建关闭钩子也很简单:

  1. 创建Thread类的一个子类
  2. 实现你自己的run方法,当应用程序(正常或者突然)关闭时,会调用此方法
  3. 在应用程序中,实例化 关闭钩子类
  4. 使用当前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中的使用的更多相关文章

  1. 关闭钩子(shutdown hook)的作用

    DK1.3介绍了java.lang.Runtime class的addShutdownHook()方法.如果你需要在你的程序关闭前采取什么措施,那么关闭钩子(shutdown hook)是很有用的. ...

  2. JAVA虚拟机关闭钩子(Shutdown Hook)

    程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码.JAVA中的ShutdownHook提供了比较好的方案. JDK提供了Java.Run ...

  3. Java关闭钩子的应用 - Shutdown Hook

    背景 在开发中,遇到这种情况,多个线程同时工作,突然一个线程遇到了fetal的错误,需要立即终止程序,等人工排查解决了问题之后重新启动.但是这样会有一个问题,程序终止时,其他线程可能正在进行重要操作, ...

  4. tomcat关闭钩子

    转 http://501565246-qq-com.iteye.com/blog/1733575 21,tomcat关闭钩子 博客分类: tomcat   在很多环境下,在关闭应用程序的时候需要做一些 ...

  5. java的关闭钩子(Shutdown Hook)

    Runtime.getRuntime().addShutdownHook(shutdownHook);    这个方法的含义说明:        这个方法的意思就是在jvm中增加一个关闭的钩子,当jv ...

  6. Spark学习:ShutdownHookManager虚拟机关闭钩子管理器

    Java程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码. JAVA中的ShutdownHook提供了比较好的方案. JDK提供了Jav ...

  7. Java Shutdown Hook 场景使用和源码分析

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 背景 ...

  8. JVM 关闭钩子

    1.功能 在jvm中添加关闭钩子(Runtime.getRuntime().addShutdownHook(shutdownHook);)后,当jvm关闭时会执行系统中已经设置的所有通过该方法添加的钩 ...

  9. 【Hook技术】实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展

    [Hook技术]实现从"任务管理器"中保护进程不被关闭 + 附带源码 + 进程保护知识扩展 公司有个监控程序涉及到进程的保护问题,需要避免用户通过任务管理器结束掉监控进程,这里使用 ...

随机推荐

  1. 搭建Django项目虚拟环境(Windows系统下)

    一.安装virtualenv 我们可以使用正式的Python环境中的pip进行安装.进入cmd界面,运行“ pip install virtualenv ”,完成安装后,可以运行“ where vir ...

  2. 【MyBatis】【SQL】删除最快纪录诞生,从一千万条记录中删除八百万条仅用2分6秒

    在 https://www.cnblogs.com/xiandedanteng/p/11669629.html 里我做个一个循环按时间查ID并删除之的程序,运行时间是4分7秒. 但是这个程序走了很多次 ...

  3. vector subscript out of range

    报这个错时会弹出一个窗口,貌似内存溢出,这是什么由于vector存放的数据超出了vector的大小所造成的. 解决方法如下: 在Vector<string> vector之后,不能直接通过 ...

  4. 一百二十六:CMS系统之轮播图管理页面布局和添加轮播图的模态对话框制作

    视图 @bp.route('/banners/')@login_required@permission_required(CMSPersmission.POSTER)def banners(): re ...

  5. COALESCE关键字的使用

    COALESCE是sql标准里面的一个关键字,我们可以和聚合函数sum,count,max等一起使用完成一些特殊的功能. 以下sql语句基于mysql 1.查询某一个列总和,如果没有数据或者NULL返 ...

  6. centos7安装redis3.2.5集群

    安装参照     https://blog.csdn.net/mingliangniwo/article/details/54600640  https://blog.csdn.net/u013820 ...

  7. MongoDB概念、安装和配置

    1.概念 分布式文档存储,高读写吞吐量,自动灾备,可伸缩. 不需要遵守严格的数据schema意味着mongodb更灵活.更适合快速开发. 2.安装 2.1 yum 安装 配置yum源 = [mongo ...

  8. 【Linux】配置SSH免密登录

    环境说明 假设我们有三台机器分别为bigdata111,bigdata112,bigdata113,三台机器均为centos 7系统. 配置SSH免密登录 (1)利用Xshell的发送键输入到所有会话 ...

  9. VS Code 的插件位置更改

    --extensions-dir 你的目标文件夹 原来的目标位置:"E:\Program Files\VSCode\Code.exe" 更改后的位置:"E:\Progra ...

  10. 利用ceph-deploy安装ceph

    手工安装https://www.jianshu.com/p/b8f085ca0307 在ceph所有节点上执行 1.配置hosts cat << EOF >> /etc/hos ...