ShutdownHooks源碼詳解
阿新 • • 發佈:2018-07-19
catch nop contains rem ava 包含 core 覆蓋 配方法 背景
在某些情況下,我們總希望在java application退出之前做一些資源清除的操作。比如:線程池,在應用關閉後仍然存活,從而造成服務宕機。而java正好給我們提供了這樣的方法來關閉這些資源,ShutdownHooks. 接下來就將其涉及到的源碼逐一解釋。
使用場景
程序正常退出
使用System.exit()
終端使用Ctrl+C觸發的中斷
系統關閉
OutOfMemory宕機
使用Kill pid命令幹掉進程(註:在使用kill -9 pid時,是不會被調用的)
Shutdown
它是關閉jvm的一個直接類,如System.exit(0). 包含JNI方法。
class Shutdown { //不能直接調用此類。通常是通過Runtime.getRuntime().addShutdownHook(thread)間接使用。 // shutdown的幾個狀態 private static final int RUNNING = 0; // application正在運行 private static final int HOOKS = 1; // 正在處理hooks private static final int FINALIZERS = 2; // 正在處理finalizers private static int state = RUNNING; // 默認為運行狀態 // hook的數量,10個。 private static final int MAX_SYSTEM_HOOKS = 10; private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS]; private static int currentRunningHook = 0; // 添加hook。 // slot表示hooks的下標,也就是執行hook的順序。 // registerShutdownInProgress表示是否在shutdown執行過程添加hook,通常是false. // hook表示的執行的線程。 static void add(int slot, boolean registerShutdownInProgress, Runnable hook) { synchronized (lock) { // 避免並發情況下的覆蓋。 if (hooks[slot] != null) // 如果hooks對應的槽點已經有對應的hook,也不能添加。 throw new InternalError("Shutdown hook at slot " + slot + " already registered"); if (!registerShutdownInProgress) { // 表示執行shutdown過程中,不添加hook. if (state > RUNNING) // 如果已經在執行shutdown操作,則不能添加hook throw new IllegalStateException("Shutdown in progress"); } else { // 如果hooks已經執行完畢,則不能再添加hook。如果正在執行hooks時,添加的槽點小於當前執行的槽點位置,則也不能添加。 if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook)) throw new IllegalStateException("Shutdown in progress"); } hooks[slot] = hook; // 對應槽點增加hook. } } // 執行所有註冊的hooks private static void runHooks() { for (int i=0; i < MAX_SYSTEM_HOOKS; i++) { try { Runnable hook; synchronized (lock) { // acquire the lock to make sure the hook registered during // shutdown is visible here. currentRunningHook = i; hook = hooks[i]; } if (hook != null) hook.run(); } catch(Throwable t) { if (t instanceof ThreadDeath) { ThreadDeath td = (ThreadDeath)t; throw td; } } } } // 執行halt操作,也就是關閉JVM的操作 static void halt(int status) { synchronized (haltLock) { halt0(status); } } static native void halt0(int status); // JNI 真正的halt方法 // Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers private static native void runAllFinalizers(); // shutdown的執行順序:runHooks > runFinalizersOnExit private static void sequence() { synchronized (lock) { if (state != HOOKS) return; } runHooks(); boolean rfoe; synchronized (lock) { state = FINALIZERS; rfoe = runFinalizersOnExit; } if (rfoe) runAllFinalizers(); } // 退出操作。runHooks > runFinalizersOnExit > halt static void exit(int status) { boolean runMoreFinalizers = false; synchronized (lock) { if (status != 0) runFinalizersOnExit = false; switch (state) { case RUNNING: state = HOOKS; break; case HOOKS: break; case FINALIZERS: if (status != 0) { halt(status); } else { runMoreFinalizers = runFinalizersOnExit; } break; } } if (runMoreFinalizers) { runAllFinalizers(); halt(status); } synchronized (Shutdown.class) { sequence(); halt(status); } } // shutdown操作,與exit不同的是,不做halt操作(關閉JVM) static void shutdown() { synchronized (lock) { switch (state) { case RUNNING: state = HOOKS; break; case HOOKS: case FINALIZERS: break; } } synchronized (Shutdown.class) { sequence(); } } }
ApplicationShutdownHooks
它是對Shutdown的一個代理類。主要是添加、刪除hook的適配。
class ApplicationShutdownHooks { private static IdentityHashMap<Thread, Thread> hooks; // hook線程的存儲器 // 初始化時,向Shutdown中添加hook.並初始化hooks容器。 static { try { Shutdown.add(1, false, new Runnable() { public void run() { runHooks(); } } ); hooks = new IdentityHashMap<>(); } catch (IllegalStateException e) { // application shutdown hooks cannot be added if // shutdown is in progress. hooks = null; } } // 添加hook 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); } // 刪除hook static synchronized boolean remove(Thread hook) { if(hooks == null) throw new IllegalStateException("Shutdown in progress"); if (hook == null) throw new NullPointerException(); return hooks.remove(hook) != null; } // 執行hook static void runHooks() { Collection<Thread> threads; synchronized(ApplicationShutdownHooks.class) { threads = hooks.keySet(); hooks = null; } for (Thread hook : threads) { hook.start(); } for (Thread hook : threads) { try { hook.join(); } catch (InterruptedException x) { } } } }
Runtime
運行時執行類。
public class Runtime { private static Runtime currentRuntime = new Runtime(); // 靜態的Runtime // 靜態方法獲取Runtime.這個也是獲取runtime的單例方法。 public static Runtime getRuntime() { return currentRuntime; } // Shutdown.exit()退出的適配方法 public void exit(int status) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkExit(status); } Shutdown.exit(status); } // 添加hook public void addShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("shutdownHooks")); } ApplicationShutdownHooks.add(hook); } // 刪除hook public boolean removeShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("shutdownHooks")); } return ApplicationShutdownHooks.remove(hook); } // 關閉程序 public void halt(int status) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkExit(status); } Shutdown.halt(status); } 。。。 }
總結:
在Shutdown的執行順序:
runHooks > runFinalizersOnExit > halt
Shutdown和ShutdownHooks不能被外部類直接調用,需要通過Runtime來代理操作。
應用實例
public class PollingServerListUpdater implements ServerListUpdater { static ScheduledThreadPoolExecutor _serverListRefreshExecutor = null; static { _serverListRefreshExecutor = new ScheduledThreadPoolExecutor(coreSize, factory); // 定時任務線程池 _shutdownThread = new Thread(new Runnable() { // shutdown hook public void run() { logger.info("Shutting down the Executor Pool for PollingServerListUpdater"); shutdownExecutorPool(); } }); Runtime.getRuntime().addShutdownHook(_shutdownThread); // 添加hook } // hook線程中的清理方法。 private static void shutdownExecutorPool() { if (_serverListRefreshExecutor != null) { _serverListRefreshExecutor.shutdown(); if (_shutdownThread != null) { try { Runtime.getRuntime().removeShutdownHook(_shutdownThread); } catch (IllegalStateException ise) { // NOPMD // this can happen if we're in the middle of a real // shutdown, // and that's 'ok' } } } } }
ShutdownHooks源碼詳解