[英]Deadlock in Java shutdown hook
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时是否需要小心,还是应该不允许编写死锁代码?
该程序处于死锁状态,因为两个线程正在请求相同的锁,而持有该锁的线程永不松手。 在上面的简单示例中,根本不需要锁定。
最初可能不清楚这里有多个线程,所以要确认这是Runtime.addShutdownHook文档中的摘录
关闭钩子只是一个已初始化但未启动的线程。 当虚拟机开始其关闭序列时,它将以未指定的顺序启动所有已注册的关闭挂钩,并使其同时运行。
另一个可能不清楚的方面是,由于死锁,对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) { }
}
}
Runtime.addShutdownHook上的javadoc对此有很多详细信息:
关机挂钩在虚拟机的生命周期中的某个微妙时刻运行,因此应进行防御性编码。 尤其应将其编写为线程安全的,并尽可能避免死锁。 他们也不应盲目依赖可能已经注册了自己的关闭钩子的服务,因此自己可能正在关闭过程中。 尝试使用其他基于线程的服务(例如AWT事件调度线程)可能会导致死锁。
Runtime.getRuntime().halt(0)
戕延迟计时器是用于避免死锁按照另一替代此文章。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.