简体   繁体   English

Java关闭挂钩中的死锁

[英]Deadlock in Java shutdown hook

System.exit() will exit JVM abruptly and graceful shutdown will not happen. System.exit()将突然退出JVM,并且不会正常关闭。 But System.exit() has hooks for graceful shutdown. 但是System.exit()具有用于正常关闭的挂钩。 But its get deadlock.. 但是它陷入僵局..

class ExitPuzzle{
    private static final Object lock = new Object();

    public static void main(String... args) {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Locking");
                synchronized (lock) {
                    System.out.println("Locked");
                }
            }
        }));
        synchronized (lock) {
            System.out.println("Exiting");
            System.exit(0);
        }
    }
}

Output is :: 输出为::

       Exiting
       Locking

Question is Why JVM is not get shutdown when System.exit(0); 问题是为什么System.exit(0)时JVM无法关闭? gets executed ? 被执行? Why its getting deadlock ? 为什么会陷入僵局? Is the developer need to take care while using ShutDowmHook in code or should it to not allow to write deadlock code ? 开发人员在代码中使用ShutDowmHook时是否需要小心,还是应该不允许编写死锁代码?

The program above deadlocks because two threads are asking for the same lock, and the one that holds the lock never lets go. 该程序处于死锁状态,因为两个线程正在请求相同的锁,而持有该锁的线程永不松手。 Within the simple example above, the lock is not required at all. 在上面的简单示例中,根本不需要锁定。

At first it may not be clear that there are multiple threads here, so to confirm this here is the snippet from the documentation of Runtime.addShutdownHook 最初可能不清楚这里有多个线程,所以要确认这是Runtime.addShutdownHook文档中的摘录

A shutdown hook is simply an initialized but unstarted thread. 关闭钩子只是一个已初始化但未启动的线程。 When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. 当虚拟机开始其关闭序列时,它将以未指定的顺序启动所有已注册的关闭挂钩,并使其同时运行。

The other aspect that may not be clear is that the call to System.exit(0) will not exit due to the dead lock. 另一个可能不清楚的方面是,由于死锁,对System.exit(0)的调用不会退出。 This is because System.exit(0) blocks until the shutdown threads have all completed. 这是因为System.exit(0)阻塞,直到关闭线程全部完成为止。 This can be confirmed by reading the code below, taken from ApplicationShutdownHooks.runHooks, and is invoked a little way into System.exit(0). 可以通过阅读下面来自ApplicationShutdownHooks.runHooks的代码来确认这一点,并在System.exit(0)中稍作调用。 I have highlighted with comments the two key lines. 我在评论中强调了两条关键线。 1) the starting of the new threads, and 2) blocking until they complete. 1)启动新线程,以及2)阻塞直到它们完成。 And as mentioned above, this join() will not return because the lock that is required by the registered shutdown hook will not be released until AFTER join() has returned. 并且如上所述,该join()将不会返回,因为在AFTER join()返回之前,注册的关闭挂钩所需的锁定不会被释放。 This circular situation is the definition of a deadlock, A cannot continue until B is finished and B cannot continue until A finishes; 这种循环情况是死锁的定义,A直到B完成才可以继续,而B直到A完成才可以继续。 thus no progress can be made. 因此无法取得进展。

static void runHooks() {
    Collection<Thread> threads;
    synchronized(ApplicationShutdownHooks.class) {
        threads = hooks.keySet();
        hooks = null;
    }

    for (Thread hook : threads) {
        hook.start();                    // STARTS THE EXTRA THREADS
    }
    for (Thread hook : threads) {
        try {
            hook.join();                 // WAITS FOR THE EXTRA THREADS TO FINISH
        } catch (InterruptedException x) { }
    }
}

The javadoc on Runtime.addShutdownHook has quite a bit of detail about this: Runtime.addShutdownHook上的javadoc对此有很多详细信息:

Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. 关机挂钩在虚拟机的生命周期中的某个微妙时刻运行,因此应进行防御性编码。 They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. 尤其应将其编写为线程安全的,并尽可能避免死锁。 They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down. 他们也不应盲目依赖可能已经注册了自己的关闭钩子的服务,因此自己可能正在关闭过程中。 Attempts to use other thread-based services such as the AWT event-dispatch thread, for example, may lead to deadlocks. 尝试使用其他基于线程的服务(例如AWT事件调度线程)可能会导致死锁。

Runtime.getRuntime().halt(0) 

戕延迟计时器是用于避免死锁按照另一替代文章。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM