Java关闭钩子死锁

时间:2014-08-08 13:26:43

标签: java

System.exit()将突然退出JVM,不会发生正常关闭。但System.exit()具有正常关闭的挂钩。但它陷入僵局..

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);
        }
    }
}

输出是::

       Exiting
       Locking

问题是为什么在System.exit(0)时JVM没有被关闭;被执行?为什么会陷入僵局?开发人员在代码中使用ShutDowmHook时是否需要注意,还是不允许编写死锁代码?

3 个答案:

答案 0 :(得分:2)

上面的程序死锁是因为两个线程要求相同的锁,并且持有锁的那个永远不会放手。在上面的简单示例中,根本不需要锁定。

起初可能并不清楚这里有多个线程,所以要确认这是Runtime.addShutdownHook文档中的片段

  

关闭钩子只是一个初始化但未启动的线程。什么时候   虚拟机开始关闭序列,它将启动所有   注册的shutdown挂钩以某种未指定的顺序挂起并让它们运行   同时进行。

另一个可能不清楚的方面是,由于死锁,对System.exit(0)的调用不会退出。这是因为System.exit(0)会阻塞,直到关闭线程全部完成。这可以通过阅读下面的代码来确认,该代码取自ApplicationShutdownHooks.runHooks,并在System.exit(0)中稍微调用一下。我用评论突出了两个关键线。 1)新线程的启动,以及2)阻塞直到它们完成。并且如上所述,此join()将不会返回,因为在返回AFTER join()之前,注册的关闭挂钩所需的锁将不会被释放。这种循环情况是死锁的定义,A在B完成之前不能继续,B在A完成之前不能继续;因此无法取得进展。

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) { }
    }
}

答案 1 :(得分:0)

Runtime.addShutdownHook上的javadoc有很多细节:

  

关闭挂钩在虚拟生命周期的微妙时间运行   机器,因此应该进行防御性编码。他们应该在   特别是,写成线程安全并避免死锁   尽可能的。他们也不应盲目依赖服务   可能已经注册了自己的关机钩子,因此可能   他们自己在关闭的过程中。试图使用其他   基于线程的服务,例如AWT event-dispatch thread,for   例如,可能会导致死锁。

答案 2 :(得分:0)

Runtime.getRuntime().halt(0) 

具有延迟计时器是根据this文章避免死锁的另一种选择。