[英]UncaughtExceptionHandler and System.exit()
I've written a custom UncaughtExceptionHandler that should print the exception to the console and shut down the application with a custom exit code.我编写了一个自定义 UncaughtExceptionHandler ,它应该将异常打印到控制台并使用自定义退出代码关闭应用程序。
The class looks like this:这个类看起来像这样:
public class FatalUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
System.out.println("Handled exception in " + t.getName() + ":");
e.printStackTrace();
System.exit(ExitCodes.UNKNOWN_EXCEPTION);
}
}
I set the UncaughtExceptionHandler in my Main.class like this:我在 Main.class 中设置 UncaughtExceptionHandler 如下:
Thread.setDefaultUncaughtExceptionHandler(new FatalUncaughtExceptionHandler());
Then I generate and start 4 threads.然后我生成并启动 4 个线程。
In one of the running threads I purposely generate a NumberFormatException
using Integer.valueOf("Test")
in order to test my Handler.在其中一个正在运行的线程中,我故意使用
Integer.valueOf("Test")
生成NumberFormatException
以测试我的处理程序。 This works fine;这很好用; Here's the output:
这是输出:
Handled exception in WatchdogThread:
java.lang.NumberFormatException: For input string: "Test"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at com.csg.gfms.gms.ctmgate.runnable.WatchdogThread.run(WatchdogThread.java:43)
Now I have a problem.现在我有一个问题。 For some reason the thread in which the exception was thrown is not being shutdown by the System.exit() command.
由于某种原因,引发异常的线程没有被 System.exit() 命令关闭。 Apparently my ShutdownHook has a lock on it.
显然我的 ShutdownHook 锁定了它。 (As seen in the output of jvisualvm):
(如 jvisualvm 的输出所示):
"WatchdogThread" #38 prio=5 os_prio=0 tid=0x000000001efa3800 nid=0xd40 in Object.wait() [0x0000000021a5e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076e30a7c0> (a com.csg.gfms.gms.ctmgate.runnable.CTMShutdownHook)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x000000076e30a7c0> (a com.csg.gfms.gms.ctmgate.runnable.CTMShutdownHook)
at java.lang.Thread.join(Thread.java:1326)
at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:107)
at java.lang.ApplicationShutdownHooks$1.run(ApplicationShutdownHooks.java:46)
at java.lang.Shutdown.runHooks(Shutdown.java:123)
at java.lang.Shutdown.sequence(Shutdown.java:167)
at java.lang.Shutdown.exit(Shutdown.java:212)
- locked <0x00000006c9605b00> (a java.lang.Class for java.lang.Shutdown)
at java.lang.Runtime.exit(Runtime.java:109)
at java.lang.System.exit(System.java:971)
at com.csg.gfms.gms.ctmgate.handlers.FatalUncaughtExceptionHandler.uncaughtException(FatalUncaughtExceptionHandler.java:13)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1057)
at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1052)
at java.lang.Thread.dispatchUncaughtException(Thread.java:1959
Even IntelliJ tells me that the System.exit command will fail.甚至 IntelliJ 也告诉我 System.exit 命令会失败。 It displays a little badge next to it saying "Method will fail" when debugging my UncaughtExceptionHandler.
在调试我的 UncaughtExceptionHandler 时,它旁边会显示一个小徽章,上面写着“方法将失败”。
This leads me to my question:这引出了我的问题:
Is it not allowed to call System.exit() from an UncaughtExceptionHandler?是否不允许从 UncaughtExceptionHandler 调用 System.exit()?
Is the shutdown hook initiated twice in my case?在我的情况下,关闭挂钩是否启动了两次?
What could be the reason for the lock on the shutdown hook?关闭挂钩上的锁定可能是什么原因?
See that com.csg.gfms
stuff in the trace?看到跟踪中的
com.csg.gfms
内容了吗?
It's not java;它不是java; it's you.
是你。 That's your code that's blocking in another shutdown hook;
那是您的代码在另一个关闭挂钩中阻塞; one that is calling
Thread.join
.一个调用
Thread.join
。
Generally when running into such weirdness, if it is at all possible to make a stand-alone super simple test case, then you should do so.一般遇到这种怪事时,如果有可能做一个独立的超级简单的测试用例,那么你应该这样做。 I have done this for you:
我已经为你做到了:
class Test {
public static void main(String[] args) throws Exception {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
System.out.println("EXITING");
System.exit(1);
}
});
for (int i = 0; i < 10; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000L);
} catch (Exception ignore) {}
throw new RuntimeException();
}
});
t.start();
}
Thread.sleep(2000L);
System.out.println("Still alive?");
}
}
When I run this, I get an arbitrary number of EXITING
prints (2 to 3, it's dependent on how many cores are simultaneously working on these threads), and then the VM hard-exits.当我运行它时,我得到任意数量的
EXITING
打印(2 到 3,这取决于有多少内核同时在这些线程上工作),然后 VM 硬退出。 Still alive?
is never printed, no locking occurs, the VM actually exits.从不打印,不发生锁定,VM 实际退出。
Thus proving that calling System.exit()
from within the uncaught exception handler is not an issue.因此证明从未捕获的异常处理程序中调用
System.exit()
不是问题。
The shutdown hook is not invoked twice;关闭钩子不会被调用两次; the shutdown hook is invoked due to you invoking
System.exit
, not because we got to the uncaught exception handler.由于您调用
System.exit
,而不是因为我们获得了未捕获的异常处理程序,因此调用了关闭挂钩。 But, if you're worried about this, hey, it's your app, print something in your shutdown hooks to be sure.但是,如果您对此感到担心,嘿,这是您的应用程序,请在关闭挂钩中打印一些内容以确保。
The lock issue is not on the shutdown hook.锁的问题是不是在关闭挂钩。 You can register any amount of shutdown hooks.
您可以注册任意数量的关闭挂钩。 It's in a shutdown hook.
这是一个在关闭挂钩。 Specifically: somebody registered an instance of
com.csg.gfms.gms.ctmgate.runnable.CTMShutdownHook
, that code is joining some thread, and that thread is not shutting down, thus that hook never exits, thus System.exit is not exiting the VM.具体来说:有人注册了一个
com.csg.gfms.gms.ctmgate.runnable.CTMShutdownHook
的实例,该代码正在加入某个线程,并且该线程没有关闭,因此该钩子永远不会退出,因此 System.exit 没有退出虚拟机。 The solution is to fix CTMShutdownHook, which is broken .解决方案是修复已损坏的 CTMShutdownHook 。
Joining a thread in a shutdown hook is... well, I'll just say it bluntly: Stupid.在关闭钩子中加入一个线程是......好吧,我直截了当地说:愚蠢。 I don't quite know what this is trying to accomplish, but the only thing I can think of is forced adherence to a bad standard.
我不太清楚这是要达到什么目的,但我唯一能想到的就是被迫遵守一个糟糕的标准。 Therefore, I can foresee that you, or the author of CTMShutdownHook, first needs some introspective on how to deal with JVM shutdowns, so that they understand that the idea underlying their implementation is fundamentally misguided and needs to be rethought.
因此,我可以预见您,或者 CTMShutdownHook 的作者,首先需要对如何处理 JVM 关闭进行一些反省,以便他们了解其实现背后的想法从根本上被误导,需要重新思考。
I will do that here.我会在这里做。
There is this mindset that to 'properly' shut down a VM, one should never invoke System.exit, one should instead carefully tell all running threads to stop, and one should carefully manage the daemon flag on all threads, so that the VM will end up shutting down on its own volition because all still alive threads have the daemon flag set.有这样一种心态,要“正确”关闭 VM,永远不要调用 System.exit,而是应该小心地告诉所有正在运行的线程停止,并且应该仔细管理所有线程上的守护程序标志,以便 VM最终会自行关闭,因为所有仍然活动的线程都设置了守护进程标志。 The argument being that this gives each thread the chance to 'shut down nicely'.
争论是这让每个线程都有机会“很好地关闭”。
This is bad code style.这是糟糕的代码风格。
Your app will just shut down if someone hits CTRL+C or otherwise asks the VM to exit, this will not result in a nice 'ask all threads to clean up and stop' process.如果有人按下 CTRL+C 或以其他方式要求 VM 退出,您的应用程序将关闭,这不会导致“要求所有线程清理并停止”进程。 In fact, your app gets zero opportunity to clean up anything if someone trips over a powercable, the computer hard-crashes, or someone terminates the app.
事实上,如果有人被电源线绊倒、计算机严重崩溃或有人终止应用程序,您的应用程序将没有机会清理任何内容。
This leads to the following rules:这导致以下规则:
kill -9
, VM core crash, memory issues, someone runs this in an IDE and just kills it, which is usually a hard-kill, the list is long) those don't run either.kill -9
、VM 内核崩溃、内存问题,有人在 IDE 中运行它并直接杀死它,这是通常是硬杀,列表很长)那些也不会运行。 By adding a shutdownhook that 'joins' a thread (joining = pause this thread until that thread exits), you've created a very silly scenario where of the 3 different ways to shut an app down:通过添加一个“加入”线程的shutdownhook(加入=暂停该线程直到该线程退出),您创建了一个非常愚蠢的场景,其中关闭应用程序的3种不同方法:
What 'join this thread in a shutdown hook' does is effectively downgrade that second form to the (bad) third form. “在关闭挂钩中加入此线程”的作用是有效地将第二种形式降级为(坏的)第三种形式。
With that context, you can now fix the broken code in CTMShutdownHook, or talk to the developer of that hook and explain to them that the elegant-sounding idea of allowing all running threads to shut down nicely is in fact bad.有了这个上下文,您现在可以修复 CTMShutdownHook 中损坏的代码,或者与该钩子的开发人员交谈并向他们解释允许所有正在运行的线程很好地关闭的听起来很优雅的想法实际上是糟糕的。
Then as a more general point of principle, shutdown hooks should block as little as possible and should definitely not wait for other threads to act.那么作为一个更一般的原则,关闭钩子应该尽可能少地阻塞并且绝对不应该等待其他线程采取行动。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.