相关文章推荐
骑白马的金针菇  ·  条件 XAML - UWP ...·  4 月前    · 
善良的筷子  ·  CREATE TABLE ...·  1 年前    · 
深情的钱包  ·  Google Chrome ...·  1 年前    · 
精彩文章免费看

java进程关闭事件监听

jvm进程如何感知关闭事件

java.lang.Shutdown

结束一个普通的java进程,一般来说可以让程序自行结束,也可以通过System.exit(n);来主动触发终止。

如果是linux系统,还可以通过外部信号来终止进程。
一般来说停止一个服务常用的方式就是 kill -2 pid(ctrl + C) kill -9 pid kill -15 pid
kill -9 可以认为操作系统从内核级别直接强行kill进程,对进程来说没有任何的准备,且无法监听-9信号。
kill -2 和 -15 则是操作系统给该进程发送一个信号通知,告知应用主动关闭,应用可以监听并接收到信号,可以完成一些关闭回收等动作,然后自我停止。
jvm专门有个 Signal Dispatcher 线程来接收信号。

Shutdown 针对这几类终止方式提供了两个处理方法。

  /* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon thread has finished.  
    Unlike the exit method, this method does not actually halt the VM. */
    static void shutdown() {
        synchronized (lock) {
            switch (state) {
            case RUNNING:       /* Initiate shutdown */
                state = HOOKS;
                break;
            case HOOKS:         /* Stall and then return */
            case FINALIZERS:
                break;
        synchronized (Shutdown.class) {
            sequence();

结合方法注释,当前进程如果所有的非守护线程执行完成,会由JNI DestroyJavaVM触发shutdown方法调用。此方法并没有halt(停止)VM。

  /* Invoked by Runtime.exit, which does all the security checks.
     * Also invoked by handlers for system-provided termination events, which should pass a nonzero status code.
    static void exit(int status) {
        boolean runMoreFinalizers = false;
        synchronized (lock) {
            if (status != 0) runFinalizersOnExit = false;
            switch (state) {
            case RUNNING:       /* Initiate shutdown */
                state = HOOKS;
                break;
            case HOOKS:         /* Stall and halt */
                break;
            case FINALIZERS:
                if (status != 0) {
                    /* Halt immediately on nonzero status */
                    halt(status);
                } else {
                    // Compatibility with old behavior: Run more finalizers and then halt
                    runMoreFinalizers = runFinalizersOnExit;
                break;
        if (runMoreFinalizers) {
            runAllFinalizers();
            halt(status);
        synchronized (Shutdown.class) {
            //Synchronize on the class object, causing any other thread that attempts to initiate shutdown to stall indefinitely
            sequence();
            halt(status);

当在代码中调用了Runtime.exit(System.exit),会调用此exit方法,status是0表示正常退出,非0表示异常退出,此方法会主动halt VM。

除了进程退出的处理方法外,在ShutDown类中,还定义了hook,允许我们在进程停止前,完成一些清场的操作。

  // The system shutdown hooks are registered with a predefined slot.
    // The list of shutdown hooks is as follows:
    // (0) Console restore hook
    // (1) Application hooks
    // (2) DeleteOnExit hook
    private static final int MAX_SYSTEM_HOOKS = 10;
    private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];

每个hook对应一个线程,会在add方法中添加,在runHooks中依次执行。

    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");
                // ... 异常场景判断
            hooks[slot] = hook;
    /* Run all registered shutdown 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;

如何添加自定义的shutdown hook

Shutdown类属于系统的操作类,并没有暴露给应用层使用。如果我们想定义自己的shutdown hook,可以使用Runtime.getRuntime().addShutdownHook()

  public void addShutdownHook(Thread hook) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("shutdownHooks"));
        ApplicationShutdownHooks.add(hook);

这个方法调用了ApplicationShutdownHooks.add(hook)

ApplicationShutdownHooks
class ApplicationShutdownHooks {
    private static IdentityHashMap<Thread, Thread> hooks;
    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;
    /* Add a new shutdown hook.  Checks the shutdown state and the hook itself, but does not do any security checks. */
    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);
    // ...

ApplicationShutdownHooks在类初始化过程完成了hook回调的注册,并初始化了IdentityHashMap,当有自定义的hook被添加时,缓存到map中。

当shutdown被触发后,会通过hook回调来调用到runHooks()

    /* Iterates over all application hooks creating a new thread for each
     * to run in. Hooks are run concurrently and this method waits for
     * them to finish.
    static void runHooks() {
        Collection<Thread> threads;
        synchronized(ApplicationShutdownHooks.class) {
            threads = hooks.keySet();
            hooks = null;
        for (Thread hook : threads) {
            hook.start();
        for (Thread hook : threads) {
            while (true) {
                try {
                    hook.join();
                    break;
                } catch (InterruptedException ignored) {

runHooks()会遍历启动的每一个hook线程,并通过join来等待所有hook执行完成。因为这个hook线程是并行操作的,所以这里无法保证hook的执行顺序。

  • jvm进程的关闭会由JNI触发Shutdown类中的exit()shutdown(), 这两个方法会调用hook回调。
  • 自定义的shutdown hook通过Runtime.getRuntime().addShutdownHook()添加到进程中。
  • 多个自定义shutdown hook并行执行,不保证执行顺序。
  •