1. 程式人生 > >ShutdownHooks源碼詳解

ShutdownHooks源碼詳解

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源碼詳解