关于如何优雅的关闭JVM

红烧土豆泥
• 阅读 1296

关于如何优雅的关闭JVM

使用的类及其方法:Runtime.addShutdownloadHook(Thread hook)

虚拟机关闭以及响应两种事件:

程序正常退出:最后一个守护线程退出或者调用exit(相当于System,exit)方法时

虚拟机响应用户中断:(键入^C)或者系统范围内的事件(用户注销或者系统关闭)而终止

          /**
         * Registers a new virtual-machine shutdown hook.
         *
         * <p> The Java virtual machine <i>shuts down</i> in response to two kinds
         * of events:
         *
         *   <ul>
         *
         *   <li> The program <i>exits</i> normally, when the last non-daemon
         *   thread exits or when the {@link #exit exit} (equivalently,
         *   {@link System#exit(int) System.exit}) method is invoked, or
         *
         *   <li> The virtual machine is <i>terminated</i> in response to a
         *   user interrupt, such as typing {@code ^C}, or a system-wide event,
         *   such as user logoff or system shutdown.
         *
         *   </ul>
         *
         * <p> A <i>shutdown hook</i> is simply an initialized but unstarted
         * thread.  When the virtual machine begins its shutdown sequence it will
         * start all registered shutdown hooks in some unspecified order and let
         * them run concurrently.  When all the hooks have finished it will then
         * halt. Note that daemon threads will continue to run during the shutdown
         * sequence, as will non-daemon threads if shutdown was initiated by
         * invoking the {@link #exit exit} method.
         *
         * <p> Once the shutdown sequence has begun it can be stopped only by
         * invoking the {@link #halt halt} method, which forcibly
         * terminates the virtual machine.
         *
         * <p> Once the shutdown sequence has begun it is impossible to register a
         * new shutdown hook or de-register a previously-registered hook.
         * Attempting either of these operations will cause an
         * {@link IllegalStateException} to be thrown.
         *
         * <p> Shutdown hooks run at a delicate time in the life cycle of a virtual
         * machine and should therefore be coded defensively.  They should, in
         * particular, be written to be thread-safe and to avoid deadlocks insofar
         * as possible.  They should also not rely blindly upon services that may
         * have registered their own shutdown hooks and therefore may themselves in
         * the process of shutting down.  Attempts to use other thread-based
         * services such as the AWT event-dispatch thread, for example, may lead to
         * deadlocks.
         *
         * <p> Shutdown hooks should also finish their work quickly.  When a
         * program invokes {@link #exit exit} the expectation is
         * that the virtual machine will promptly shut down and exit.  When the
         * virtual machine is terminated due to user logoff or system shutdown the
         * underlying operating system may only allow a fixed amount of time in
         * which to shut down and exit.  It is therefore inadvisable to attempt any
         * user interaction or to perform a long-running computation in a shutdown
         * hook.
         *
         * <p> Uncaught exceptions are handled in shutdown hooks just as in any
         * other thread, by invoking the
         * {@link ThreadGroup#uncaughtException uncaughtException} method of the
         * thread's {@link ThreadGroup} object. The default implementation of this
         * method prints the exception's stack trace to {@link System#err} and
         * terminates the thread; it does not cause the virtual machine to exit or
         * halt.
         *
         * <p> In rare circumstances the virtual machine may <i>abort</i>, that is,
         * stop running without shutting down cleanly.  This occurs when the
         * virtual machine is terminated externally, for example with the
         * {@code SIGKILL} signal on Unix or the {@code TerminateProcess} call on
         * Microsoft Windows.  The virtual machine may also abort if a native
         * method goes awry by, for example, corrupting internal data structures or
         * attempting to access nonexistent memory.  If the virtual machine aborts
         * then no guarantee can be made about whether or not any shutdown hooks
         * will be run.
         *
         * @param   hook
         *          An initialized but unstarted {@link Thread} object
         *
         * @throws  IllegalArgumentException
         *          If the specified hook has already been registered,
         *          or if it can be determined that the hook is already running or
         *          has already been run
         *
         * @throws  IllegalStateException
         *          If the virtual machine is already in the process
         *          of shutting down
         *
         * @throws  SecurityException
         *          If a security manager is present and it denies
         *          {@link RuntimePermission}("shutdownHooks")
         *
         * @see #removeShutdownHook
         * @see #halt(int)
         * @see #exit(int)
         * @since 1.3
         */

    public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        ApplicationShutdownHooks.add(hook);
    }

    //获取系统安全接口。
    //返回:如果已经为当前应用程序建立了安全管理器,则返回该安全管理器; 否则,返回null 。
    public static SecurityManager getSecurityManager() {
            return security;
        }

        /**
         * Throws a <code>SecurityException</code> if the requested
         * access, specified by the given permission, is not permitted based
         * on the security policy currently in effect.
         * <p>
         * This method calls <code>AccessController.checkPermission</code>
         * with the given permission.
         *
         * @param     perm   the requested permission.
         * @exception SecurityException if access is not permitted based on
         *            the current security policy.
         * @exception NullPointerException if the permission argument is
         *            <code>null</code>.
         * @since     1.2
         */
        /**
          * (来自百度翻译)
          *    如果基于当前有效的安全策略不允许给定权限指定的请求访问,则抛出SecurityException 。
          * 此方法使用给定的权限调用AccessController.checkPermission 。
          *    参数:perm - 请求的权限。
          */
    public void checkPermission(Permission perm) {
            java.security.AccessController.checkPermission(perm);
        }

    //此处的hook,即为先前在调用addShutdownHook时传入的线程
    //hooks = new IdentityHashMap<>();
    private ApplicationShutdownHooks() {}
        /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
         * but does not do any security checks.
         */
        static synchronized void add(Thread hook) {
            if(hooks == null)
                throw new IllegalStateException("Shutdown in progress");

            if (hook.isAlive())
                throw new IllegalArgumentException("Hook already running");

            if (hooks.containsKey(hook))
                throw new IllegalArgumentException("Hook previously registered");

            hooks.put(hook, hook);
        }

以下来自百度翻译的关于addShutdownHook()方法的描述

关闭钩子在虚拟机生命周期的一个微妙时刻运行,因此应该进行防御性编码。 特别是,它们应该被编写为线程安全的,并尽可能避免死锁。 他们也不应该盲目依赖可能已经注册了自己的关闭钩子的服务,因此他们自己可能会在关闭过程中。 例如,尝试使用其他基于线程的服务(例如 AWT 事件分派线程)可能会导致死锁

关闭钩子也应该快速完成它们的工作。 当程序调用exit ,期望虚拟机将立即关闭并退出。 当虚拟机由于用户注销或系统关闭而终止时,底层操作系统可能只允许关闭和退出的固定时间量。 因此,不建议尝试任何用户交互或在关闭挂钩中执行长时间运行的计算

通过调用线程的ThreadGroup对象的uncaughtException方法,在关闭钩子中处理未捕获的异常就像在任何其他线程中一样。 此方法的默认实现将异常的堆栈跟踪打印到System.err并终止线程; 它不会导致虚拟机退出或停止。

在极少数情况下,虚拟机可能会中止,即在没有彻底关闭的情况下停止运行。 当虚拟机从外部终止时会发生这种情况,例如在 Unix 上使用SIGKILL信号或在 Microsoft Windows 上使用TerminateProcess调用。 如果本地方法出错,例如破坏内部数据结构或尝试访问不存在的内存,虚拟机也可能中止。 如果虚拟机中止,则无法保证是否会运行任何关闭挂钩

案例:

  /**
 * 优雅的关闭JVM
 */
public class ShutDownByElegance {
    public static void main(String[] args) {
        AtomicInteger integer = new AtomicInteger(0);
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + "==>" + i);
                    integer.addAndGet(1);
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread_01");

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + "==>" + i);
                    integer.addAndGet(1);
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread_02");

        Thread thread3 = new Thread(() -> {

            System.out.println("哇卡卡卡卡~,所有事情整完了,终于可以结束了,让我们瞅瞅共循环了几次吧!。");
            System.out.println(integer);

        }, "Thread_03");

        thread2.setDaemon(true);
        thread1.start();
        thread2.start();

        /**
         * 此种方式只能保证在正常结束时,在最后执行thread3
         */
        Runtime.getRuntime().addShutdownHook(thread3);
    }
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这