浅析java的退出钩子(Hook)

  • 钩子作用是啥
    当你怕退出jvm时中断应用正在处理的任务,从而导致各种问题时。此时钩子就派上了用场。当然你直接拔电源、kill -9再牛逼的机制也不管用了。
  • 怎么用
public static void main(String[] args) {   

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hook shut down");
            }
        }));
        System.out.println("123");
    }

运行这段代码输出

123
hook shut down
  • 啥原理
    首先,将我们新实例化的线程作为参数调用ApplicationShutdownHooks的add方法
public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        }
        ApplicationShutdownHooks.add(hook);
    }
    

再来看add方法中经过一些校验之后将其放到map中,那么这个hooks的map怎么来的

private static IdentityHashMap hooks;
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);
    }

我们都知道类的初始化早于实例化,那么static代码块首先运行,肯定就回创建map对象,此时在static中又调用了 Shutdown的add方法,我们再继续看

ApplicationShutdownHooks类
static {
        try {
            Shutdown.add(1 /* shutdown hook invocation order */,
                false /* not registered if shutdown in progress */,
                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;
        }
    }

会将ApplicationShutdownHooks类实例化的Runnable添加到Shutdown类的hooks中。

Shutdown类
static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
        synchronized (lock) {
            if (hooks[slot] != null)
                throw new InternalError("Shutdown hook at slot " + slot + " already registered");

            if (!registerShutdownInProgress) {
                if (state > RUNNING)
                    throw new IllegalStateException("Shutdown in progress");
            } else {
                if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
                    throw new IllegalStateException("Shutdown in progress");
            }

            hooks[slot] = hook;
        }
    }

其次,经过以上步骤已经将钩子添加了,那么当jvm退出时是怎么触发的。
当我们运行上述代码时在
java.lang.ApplicationShutdownHooks#runHooks中打断点,然后如下图,
浅析java的退出钩子(Hook)_第1张图片 此时我们的main线程已退出,一个叫DestroyJavaVM的线程被激活,那么这个DestroyJavaVM是怎么被激活的呢。
《Java性能优化权威指南》中有如下一段解释,而上图中的调用栈也清晰的说明了调用顺序。

最后,经过以上步骤,我们添加的hook被调用,完成了退出jvm时优雅的关闭服务。

你可能感兴趣的:(java基础)