关闭钩子

  在很多实际应用环境中,当用户关了应用程序时,需要做一些善后清理工作,但问题是,用户有时并不会按照推荐的方法关闭应用程序,很有可能不做清理工作,例如在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”输出到控制台上,但是可以插入想在应用程序关闭之前的任何代码。

 1 package myex16.pyrmont.shutdownhook;
 2
 3 import java.io.IOException;
 4
 5 /**
 6  * <p>
 7  * <b>Title:ShutdownHookDemo.java</b>
 8  * </p>
 9  * <p>
10  * Copyright:ChenDong 2018
11  * </p>
12  * <p>
13  * Company:仅学习时使用
14  * </p>
15  * <p>
16  * 类功能描述: 演示 关闭钩子 的简单实用
17  * </p>
18  *
19  * @author 陈东
20  * @date 2018年12月24日 下午8:01:14
21  * @version 1.0
22  */
23 public class ShutdownHookDemo {
24
25     public void start() {
26         System.out.println("Demo start");
27         // 创建关闭钩子 就是线程
28         ShutdownHook shutdownHook = new ShutdownHook();
29         // 像虚拟机中注册关闭钩子
30         Runtime.getRuntime().addShutdownHook(shutdownHook);
31     }
32
33     /**
34      *
35      * <p>
36      * Title: main
37      * </p>
38      *
39      * @date 2018年12月24日 下午8:01:15
40      *
41      *       <p>
42      *       功能描述:
43      *       </p>
44      *
45      * @param args
46      *
47      */
48     public static void main(String[] args) {
49         ShutdownHookDemo demo = new ShutdownHookDemo();
50         demo.start();
51
52         try {
53             // 等待输入 这样 线程就不会走完 然后只要随便输入东西 就会 走完流程 测试在线程运行完之后 虚拟机启动我们注册的关闭钩子 并运行
54             System.in.read();
55         } catch (IOException e) {
56             // TODO Auto-generated catch block
57             e.printStackTrace();
58         }
59     }
60
61 }
62
63 class ShutdownHook extends Thread {
64     public void run() {
65         System.out.println("Shutting down");
66     }
67 }

运行结果

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,效果如图

该应用程序会在它启动时创建一个临时文件,并在关闭时删除该临时文件。

  1 package myex16.pyrmont.shutdownhook;
  2
  3 import java.awt.Rectangle;
  4 import java.awt.event.ActionEvent;
  5 import java.io.File;
  6 import java.io.IOException;
  7
  8 import javax.swing.JButton;
  9 import javax.swing.JFrame;
 10 import javax.swing.JTextArea;
 11
 12 /**
 13  * <p>
 14  * <b>Title:MySwingApp.java</b>
 15  * </p>
 16  * <p>
 17  * Copyright:ChenDong 2018
 18  * </p>
 19  * <p>
 20  * Company:仅学习时使用
 21  * </p>
 22  * <p>
 23  * 类功能描述:演示 关闭钩子的使用
 24  * </p>
 25  *
 26  * @author 陈东
 27  * @date 2018年12月24日 下午8:27:54
 28  * @version 1.0
 29  */
 30 public class MySwingApp extends JFrame {
 31
 32     JButton exitButton = new JButton();
 33
 34     JTextArea jTextArea1 = new JTextArea();
 35
 36     String dir = System.getProperty("user.dir");
 37     String filename = "temp.txt";
 38
 39     public MySwingApp() {
 40         exitButton.setText("Exit");
 41         exitButton.setBounds(new Rectangle(304, 248, 76, 37));
 42         exitButton.addActionListener(new java.awt.event.ActionListener() {
 43
 44             @Override
 45             public void actionPerformed(ActionEvent e) {
 46                 exitButton_actionPerformed(e);
 47             }
 48
 49         });
 50
 51         this.getContentPane().setLayout(null);
 52         jTextArea1.setText("Click the Exit button to quit");
 53         jTextArea1.setBounds(new Rectangle(9, 7, 371, 235));
 54         this.getContentPane().add(exitButton, null);
 55         this.getContentPane().add(jTextArea1, null);
 56         this.setDefaultCloseOperation(EXIT_ON_CLOSE);
 57         this.setBounds(0, 0, 400, 330);
 58         this.setVisible(true);
 59         initialize();
 60     }
 61
 62     private void initialize() {
 63         // 创建一个temp.txt文件
 64         File file = new File(dir, filename);
 65         try {
 66             System.out.println("Creating temporary file");
 67             file.createNewFile();
 68         } catch (IOException e) {
 69             System.out.println("Failed creating temporary file.");
 70         }
 71     }
 72
 73     /**
 74      *
 75      * <p>
 76      * Title: main
 77      * </p>
 78      *
 79      * @date 2018年12月24日 下午8:27:54
 80      *
 81      *       <p>
 82      *       功能描述:
 83      *       </p>
 84      *
 85      * @param args
 86      *
 87      */
 88     public static void main(String[] args) {
 89
 90         MySwingApp mySwingApp = new MySwingApp();
 91     }
 92
 93     private void shutdown() {
 94         // 删除这个文件
 95         File file = new File(dir, filename);
 96         if (file.exists()) {
 97             System.out.println("Deleting temporary file.");
 98             file.delete();
 99         }
100     }
101
102     void exitButton_actionPerformed(ActionEvent e) {
103         shutdown();
104         System.exit(0);
105     }
106
107 }

  在实例化这个类时,应用程序会调用其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 之后,虚拟机执行的关闭钩子

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类中,

有时候,应用程序在关闭之前应该执行一些代码清理工作,但是你不能价设定用户总是正常退出,那么这次介绍的关闭钩子提供了一种解决方案,确保无论用户如何关闭应用程序,清理代码总是能得到执行。

原文地址:https://www.cnblogs.com/ChenD/p/10171001.html

时间: 2024-11-13 07:28:44

关闭钩子的相关文章

JVM 关闭钩子

1.功能 在jvm中添加关闭钩子(Runtime.getRuntime().addShutdownHook(shutdownHook);)后,当jvm关闭时会执行系统中已经设置的所有通过该方法添加的钩子,系统执行完这些钩子后,jvm才会关闭.所以这些钩子可以在jvm关闭的时候进行内存清理.对象销毁.关闭I/O资源等操作. 2.示例 示例1及输出: package cn.edu.buaa.jvmhook; /** * Runtime.getRuntime().addShutdownHook(shu

java的关闭钩子(Shutdown Hook)

Runtime.getRuntime().addShutdownHook(shutdownHook); 这个方法的含义说明: 这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭.所以这些钩子可以在jvm关闭的时候进行内存清理.对象销毁等操作. 用途 1应用程序正常退出,在退出时执行特定的业务逻辑,或者关闭资源等操作.   2虚拟机非正常退出,比如用户按下ctrl+c

Tomcat 关闭钩子

使用JAVA的过程中,经常遇到程序启动时初始化一下资源,或生成一下临时文件,程序退出时要清除这些临时文件,或者程序退出时执行一下必要的其他操作.如果程序是通过我们提供的关闭/退出按钮正常退出的,一切还都好处理,但是如果用户直接关闭虚拟机运行的窗口,那一切就会变的比较复杂. 好在java提供了一种优雅的方式去解决这种问题.使得关闭的善后处理的代码能执行.java的关闭钩子能确保总是执行,无论用户如何终止应用程序.除非用户kill,这个是个死穴. 对java而言,虚拟机会对以下几种操作进行关闭: (

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

Java程序经常也会遇到进程挂掉的情况,一些状态没有正确的保存下来,这时候就需要在JVM关掉的时候执行一些清理现场的代码.JAVA中的ShutdownHook提供了比较好的方案. JDK提供了Java.Runtime.addShutdownHook(Thread hook)方法,可以注册一个JVM关闭的钩子,这个钩子可以在一下几种场景中被调用: 程序正常退出 使用System.exit() 终端使用Ctrl+C触发的中断 系统关闭 OutOfMemory宕机 使用Kill pid命令干掉进程(注

tomat(16)关闭钩子

[0]REAMDE 0)本文部分文字描述转自:"how tomcat works",旨在学习"tomat(16)关闭钩子"的相关知识: 1)problem+solution: 1.1)problem:在很多实际环境中,当用户关闭应用程序时,并不会按照推荐的方法关闭应用程序,很有可能不做清理工作: 1.2)solution:java 为程序员提供了一种优雅的方法可以在在关闭过程中执行一些代码,以确保那些负责善后处理的代码可能能够执行: 2)在java中,虚拟机会对两类

Java关闭钩子

Java关闭钩子 在Java程序中可以通过添加关闭钩子,实现在程序退出时关闭资源的功能. 使用Runtime.addShutdownHook(Thread hook)向JVM添加关闭钩子 public void addShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("shu

Java关闭钩子的应用

内容:在很多实际的应用环境中,当用户关闭应用程序时,需要做一些善后清理工作.但问题是,用户有时并不会按照推荐的方法关闭程序,很多可能不做清理工作.关闭钩子是一种解决方案,确保无论用户如果关闭应用程序,清理代码总能够得到执行.我们通过向Runtime类注册关闭钩子:是一个已初始化但尚未启动的线程.虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行. public class ShutdownHookDemo { public void start() {

Tomcat 学习进阶历程之关闭钩子

使用JAVA的过程中,经常遇到程序启动时初始化一下资源,或生成一下临时文件,程序退出时要清除这些临时文件,或者程序退出时执行一下必要的其他操作.如果程序是通过我们提供的关闭/退出按钮正常退出的,一切还都好处理,但是如果用户直接关闭虚拟机运行的窗口,那一切就会变的比较复杂. 好在java提供了一种优雅的方式去解决这种问题.使得关闭的善后处理的代码能执行.java的关闭钩子能确保总是执行,无论用户如何终止应用程序.除非用户kill,这个是个死穴. 对java而言,虚拟机会对以下几种操作进行关闭: (

Java关闭钩子的应用 - Shutdown Hook

背景 在开发中,遇到这种情况,多个线程同时工作,突然一个线程遇到了fetal的错误,需要立即终止程序,等人工排查解决了问题之后重新启动.但是这样会有一个问题,程序终止时,其他线程可能正在进行重要操作,比如发一个message到另一个模块,并更新数据库状态.突然终止,可能会让这个操作只完成一半,从而导致数据不一致. 解决方案是:参考数据库Transaction原子性的概念,将这一系列重要操作看作一个整体,要么全部完成,要么全部不完成.为方便表述,我们把这一系列重要操作记为操作X. 当程序即将退出时