ShutdownHooks源码详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ShutdownHooks源码详解相关的知识,希望对你有一定的参考价值。

背景

  在某些情况下,我们总希望在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源码详解的主要内容,如果未能解决你的问题,请参考以下文章

Java线程池详解

(转) Java中的负数及基本类型的转型详解

详解Android WebView加载html片段

C# 获得目录创建时间的源码片段

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段