继之前的文章 详解JVM如何处理异常,今天再次发布一篇比较关联的文章,如题目可知,今天聊一聊在JVM中线程遇到未捕获异常的问题,其中涉及到线程如何处理未捕获异常和一些内容介绍。
未捕获异常指的是我们在方法体中没有使用try-catch捕获的异常,比如下面的例子
1 2 3 4 5 6 7 | private static void testUncaughtException(String arg) { try { System.out.println(1 / arg.length()); } catch (ArithmeticException e) { e.printStackTrace(); } } |
---|
上面的代码很有可能发生如下情况
对于上面的问题,我们不难发现
void uncaughtException(Thread t, Throwable e);
用来处理接收处理异常发生后的操作,比如收集崩溃信息并上报等Thread.setUncaughtExceptionHandler
为某一个Thread实例设置未捕获异常处理者Thread.setDefaultUncaughtExceptionHandler
设置所有Thread实例的未捕获异常处理者当线程A中出现了未捕获异常时,JVM会调用线程A的dispatchUncaughtException(Throwable)
方法
1 2 3 4 5 6 7 | /** * Dispatch an uncaught exception to the handler. This method is * intended to be called only by the JVM. */ private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); } |
---|
1 2 3 4 5 6 7 8 9 10 11 12 13 | /** * Returns the handler invoked when this thread abruptly terminates * due to an uncaught exception. If this thread has not had an * uncaught exception handler explicitly set then this thread's * <tt>ThreadGroup</tt> object is returned, unless this thread * has terminated, in which case <tt>null</tt> is returned. * @since 1.5 * @return the uncaught exception handler for this thread */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } |
---|
Thread.getDefaultUncaughtExceptionHandler()
并分发1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /** * Called by the Java Virtual Machine when a thread in this * thread group stops because of an uncaught exception, and the thread * does not have a specific {@link Thread.UncaughtExceptionHandler} * installed. * <p> * The <code>uncaughtException</code> method of * <code>ThreadGroup</code> does the following: * <ul> * <li>If this thread group has a parent thread group, the * <code>uncaughtException</code> method of that parent is called * with the same two arguments. * <li>Otherwise, this method checks to see if there is a * {@linkplain Thread#getDefaultUncaughtExceptionHandler default * uncaught exception handler} installed, and if so, its * <code>uncaughtException</code> method is called with the same * two arguments. * <li>Otherwise, this method determines if the <code>Throwable</code> * argument is an instance of {@link ThreadDeath}. If so, nothing * special is done. Otherwise, a message containing the * thread's name, as returned from the thread's {@link * Thread#getName getName} method, and a stack backtrace, * using the <code>Throwable</code>'s {@link * Throwable#printStackTrace printStackTrace} method, is * printed to the {@linkplain System#err standard error stream}. * </ul> * <p> * Applications can override this method in subclasses of * <code>ThreadGroup</code> to provide alternative handling of * uncaught exceptions. * * @param t the thread that is about to exit. * @param e the uncaught exception. * @since JDK1.0 */ public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); } else { Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print("Exception in thread \"" + t.getName() + "\" "); e.printStackTrace(System.err); } } } |
---|
将上面的处理流程做成图的形式,就是下图所示
注:上述图片来自https://www.javamex.com/tutorials/exceptions/exceptions_uncaught_handler.shtml
上面提到了初始的ThreadGroup没有父ThreadGroup,是主线程所在的ThreadGroup么?
这个问题,我们可以通过这样一段代码验证
1 2 3 4 5 6 7 | private static void dumpThreadGroups() { ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); while(threadGroup != null) { System.out.println("dumpThreadGroups threadGroup=" + threadGroup.getName()); threadGroup = threadGroup.getParent(); } } |
---|
执行该方法对应的输出是
1 2 | dumpThreadGroups threadGroup=main dumpThreadGroups threadGroup=system |
---|
因此我们可以发现,初始的ThreadGroup是一个叫做system的ThreadGroup,而不是main ThreadGroup
这其实是一个很好的问题,答案是不一定会被调用,因为可能存在以下的情况