关于如何优雅的关闭JVM
使用的类及其方法:Runtime.addShutdownloadHook(Thread hook)
虚拟机关闭以及响应两种事件:
程序正常退出:最后一个守护线程退出或者调用exit(相当于System,exit)方法时
虚拟机响应用户中断:(键入^C)或者系统范围内的事件(用户注销或者系统关闭)而终止
1 /** 2 * Registers a new virtual-machine shutdown hook. 3 * 4 * <p> The Java virtual machine <i>shuts down</i> in response to two kinds 5 * of events: 6 * 7 * <ul> 8 * 9 * <li> The program <i>exits</i> normally, when the last non-daemon 10 * thread exits or when the {@link #exit exit} (equivalently, 11 * {@link System#exit(int) System.exit}) method is invoked, or 12 * 13 * <li> The virtual machine is <i>terminated</i> in response to a 14 * user interrupt, such as typing {@code ^C}, or a system-wide event, 15 * such as user logoff or system shutdown. 16 * 17 * </ul> 18 * 19 * <p> A <i>shutdown hook</i> is simply an initialized but unstarted 20 * thread. When the virtual machine begins its shutdown sequence it will 21 * start all registered shutdown hooks in some unspecified order and let 22 * them run concurrently. When all the hooks have finished it will then 23 * halt. Note that daemon threads will continue to run during the shutdown 24 * sequence, as will non-daemon threads if shutdown was initiated by 25 * invoking the {@link #exit exit} method. 26 * 27 * <p> Once the shutdown sequence has begun it can be stopped only by 28 * invoking the {@link #halt halt} method, which forcibly 29 * terminates the virtual machine. 30 * 31 * <p> Once the shutdown sequence has begun it is impossible to register a 32 * new shutdown hook or de-register a previously-registered hook. 33 * Attempting either of these operations will cause an 34 * {@link IllegalStateException} to be thrown. 35 * 36 * <p> Shutdown hooks run at a delicate time in the life cycle of a virtual 37 * machine and should therefore be coded defensively. They should, in 38 * particular, be written to be thread-safe and to avoid deadlocks insofar 39 * as possible. They should also not rely blindly upon services that may 40 * have registered their own shutdown hooks and therefore may themselves in 41 * the process of shutting down. Attempts to use other thread-based 42 * services such as the AWT event-dispatch thread, for example, may lead to 43 * deadlocks. 44 * 45 * <p> Shutdown hooks should also finish their work quickly. When a 46 * program invokes {@link #exit exit} the expectation is 47 * that the virtual machine will promptly shut down and exit. When the 48 * virtual machine is terminated due to user logoff or system shutdown the 49 * underlying operating system may only allow a fixed amount of time in 50 * which to shut down and exit. It is therefore inadvisable to attempt any 51 * user interaction or to perform a long-running computation in a shutdown 52 * hook. 53 * 54 * <p> Uncaught exceptions are handled in shutdown hooks just as in any 55 * other thread, by invoking the 56 * {@link ThreadGroup#uncaughtException uncaughtException} method of the 57 * thread's {@link ThreadGroup} object. The default implementation of this 58 * method prints the exception's stack trace to {@link System#err} and 59 * terminates the thread; it does not cause the virtual machine to exit or 60 * halt. 61 * 62 * <p> In rare circumstances the virtual machine may <i>abort</i>, that is, 63 * stop running without shutting down cleanly. This occurs when the 64 * virtual machine is terminated externally, for example with the 65 * {@code SIGKILL} signal on Unix or the {@code TerminateProcess} call on 66 * Microsoft Windows. The virtual machine may also abort if a native 67 * method goes awry by, for example, corrupting internal data structures or 68 * attempting to access nonexistent memory. If the virtual machine aborts 69 * then no guarantee can be made about whether or not any shutdown hooks 70 * will be run. 71 * 72 * @param hook 73 * An initialized but unstarted {@link Thread} object 74 * 75 * @throws IllegalArgumentException 76 * If the specified hook has already been registered, 77 * or if it can be determined that the hook is already running or 78 * has already been run 79 * 80 * @throws IllegalStateException 81 * If the virtual machine is already in the process 82 * of shutting down 83 * 84 * @throws SecurityException 85 * If a security manager is present and it denies 86 * {@link RuntimePermission}("shutdownHooks") 87 * 88 * @see #removeShutdownHook 89 * @see #halt(int) 90 * @see #exit(int) 91 * @since 1.3 92 */ 93 94 public void addShutdownHook(Thread hook) { 95 SecurityManager sm = System.getSecurityManager(); 96 if (sm != null) { 97 sm.checkPermission(new RuntimePermission("shutdownHooks")); 98 } 99 ApplicationShutdownHooks.add(hook); 100 } 101 102 //获取系统安全接口。 103 //返回:如果已经为当前应用程序建立了安全管理器,则返回该安全管理器; 否则,返回null 。 104 public static SecurityManager getSecurityManager() { 105 return security; 106 } 107 108 /** 109 * Throws a <code>SecurityException</code> if the requested 110 * access, specified by the given permission, is not permitted based 111 * on the security policy currently in effect. 112 * <p> 113 * This method calls <code>AccessController.checkPermission</code> 114 * with the given permission. 115 * 116 * @param perm the requested permission. 117 * @exception SecurityException if access is not permitted based on 118 * the current security policy. 119 * @exception NullPointerException if the permission argument is 120 * <code>null</code>. 121 * @since 1.2 122 */ 123 /** 124 * (来自百度翻译) 125 * 如果基于当前有效的安全策略不允许给定权限指定的请求访问,则抛出SecurityException 。 126 * 此方法使用给定的权限调用AccessController.checkPermission 。 127 * 参数:perm - 请求的权限。 128 */ 129 public void checkPermission(Permission perm) { 130 java.security.AccessController.checkPermission(perm); 131 } 132 133 //此处的hook,即为先前在调用addShutdownHook时传入的线程 134 //hooks = new IdentityHashMap<>(); 135 private ApplicationShutdownHooks() {} 136 /* Add a new shutdown hook. Checks the shutdown state and the hook itself, 137 * but does not do any security checks. 138 */ 139 static synchronized void add(Thread hook) { 140 if(hooks == null) 141 throw new IllegalStateException("Shutdown in progress"); 142 143 if (hook.isAlive()) 144 throw new IllegalArgumentException("Hook already running"); 145 146 if (hooks.containsKey(hook)) 147 throw new IllegalArgumentException("Hook previously registered"); 148 149 hooks.put(hook, hook); 150 } 151
以下来自百度翻译的关于addShutdownHook()方法的描述
关闭钩子在虚拟机生命周期的一个微妙时刻运行,因此应该进行防御性编码。 特别是,它们应该被编写为线程安全的,并尽可能避免死锁。 他们也不应该盲目依赖可能已经注册了自己的关闭钩子的服务,因此他们自己可能会在关闭过程中。 例如,尝试使用其他基于线程的服务(例如 AWT 事件分派线程)可能会导致死锁
关闭钩子也应该快速完成它们的工作。 当程序调用exit ,期望虚拟机将立即关闭并退出。 当虚拟机由于用户注销或系统关闭而终止时,底层操作系统可能只允许关闭和退出的固定时间量。 因此,不建议尝试任何用户交互或在关闭挂钩中执行长时间运行的计算
通过调用线程的ThreadGroup对象的uncaughtException方法,在关闭钩子中处理未捕获的异常就像在任何其他线程中一样。 此方法的默认实现将异常的堆栈跟踪打印到System.err并终止线程; 它不会导致虚拟机退出或停止。
在极少数情况下,虚拟机可能会中止,即在没有彻底关闭的情况下停止运行。 当虚拟机从外部终止时会发生这种情况,例如在 Unix 上使用SIGKILL信号或在 Microsoft Windows 上使用TerminateProcess调用。 如果本地方法出错,例如破坏内部数据结构或尝试访问不存在的内存,虚拟机也可能中止。 如果虚拟机中止,则无法保证是否会运行任何关闭挂钩
案例:
1 /** 2 * 优雅的关闭JVM 3 */ 4public class ShutDownByElegance { 5 public static void main(String[] args) { 6 AtomicInteger integer = new AtomicInteger(0); 7 Thread thread1 = new Thread(() -> { 8 for (int i = 0; i < 100; i++) { 9 try { 10 System.out.println(Thread.currentThread().getName() + "==>" + i); 11 integer.addAndGet(1); 12 Thread.sleep(20); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 } 17 }, "Thread_01"); 18 19 Thread thread2 = new Thread(() -> { 20 for (int i = 0; i < 100; i++) { 21 try { 22 System.out.println(Thread.currentThread().getName() + "==>" + i); 23 integer.addAndGet(1); 24 Thread.sleep(50); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 } 29 }, "Thread_02"); 30 31 Thread thread3 = new Thread(() -> { 32 33 System.out.println("哇卡卡卡卡~,所有事情整完了,终于可以结束了,让我们瞅瞅共循环了几次吧!。"); 34 System.out.println(integer); 35 36 }, "Thread_03"); 37 38 thread2.setDaemon(true); 39 thread1.start(); 40 thread2.start(); 41 42 /** 43 * 此种方式只能保证在正常结束时,在最后执行thread3 44 */ 45 Runtime.getRuntime().addShutdownHook(thread3); 46 } 47} 48
