线程未捕获的异常的处理

线程未捕获异常处理

线程中run方法出现异常会导致线程终止运行,可以在run方法中使用try-catch捕获发生的异常,对于run方法中没有用try-catch捕获到的异常,如果想要对其进行处理,需要给线程设置异常捕获处理器。

线程中抛出的异常并不能被如下形式的try-catch捕获到:

1
2
3
4
5
6
7
8
9
10
try{
new Thread(new Runnable() {
@Override
public void run() {
int i=1/0;
}
}).start();
}catch (Exception e){
System.out.println("error:"+e.getMessage());
}

Thread中的运行时异常并不能被catch捕获,System.out.println(“error:”+e.getMessage())并没有执行。

如果我们想要对Thread中抛出的未被捕获的异常进行处理,我们可以给线程设置异常捕获处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
int i=1/0;
}
});
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("error:"+e.getMessage());
}
});
thread.start();

线程中抛出的异常会被setUncaughtExceptionHandler方法设置的异常捕获处理器捕获, System.out.println(“error:”+e.getMessage())获得执行。

我们可以给每个线程的设置具体的未捕获异常处理器,也可以给所有的线程设置默认异常处理器。通过

1
2
3
4
5
6
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {

}
});

可以给所有线程设置默认的处理器。给所有的线程设置了默认的处理器后,对于没有设置特定的处理器的线程,当线程中出现未捕获的异常的时候,会使用默认的处理器来处理线程中出现的未捕获的异常。

如果没有给线程设置特定的处理器,那么线程的处理器就是线程的ThreadGroup对象,我们通过Thread.setDefaultUncaughtExceptionHandler设置的异常处理器的uncaughtException方法会在ThreadGroup对象的uncaughtException方法中被调用,具体看下一小节源码分析。

未捕获异常处理流程代码分析

当线程中出现未捕获异常后JVM将调用Thread中的dispatchUncaughtException方法把异常传递给线程的未捕获异常处理器。

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

dispatchUncaughtException这个方法只是提供给JVM调用的。

1
2
3
4
5
6
7
8
9
10
11
/* 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;
}

从代码可以看出如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。ThreadGroup类定义:

1
class ThreadGroup implements Thread.UncaughtExceptionHandler

ThreadGroup实现的uncaughtException如下:

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

线程组处理未捕获异常,首先将异常消息通知给父线程组;如果父线程组为空,尝试利用一个默认的defaultUncaughtExceptionHandler获得异常处理器来处理异常,Thread.getDefaultUncaughtExceptionHandler()获得的异常捕获处理器就是我们之前通过Thread.getDefaultUncaughtExceptionHandler()设置的处理器;如果没有默认的异常处理器则将错误信息输出到System.err。

如果不给线程设置线程组,仍然可以正常的使用默认异常处理器。所有的线程都会有线程组,即使我们在构造线程时显示的传入null值的线程组,最终JVM也会为我们分配到一个线程组。Thread的init方法决定了这个特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}

##几个小例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("I catch a exception from " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());
}
});

ThreadGroup myGroup = new ThredGroup("ThreadGroup");

new Thread(myGroup, new Runnable() {
@Override
public void run() {
int i = 1/0;
}
}, "thread1").start();

new Thread(myGroup, new Runnable() {
@Override
public void run() {
int i = 1/0;
}
}, "thread2").start();

打印结果如下:

1
2
I catch a exception from  thread1:myGroup
I catch a exception from thread2:myGroup

通过自定义ThreadGrop改变上述机制。

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
class OtherGroup extends ThreadGroup{
public OtherGroup(String name) {
super(name);
}

@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("I am a other group and do nothing");
}
}

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("I catch a exception from " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());
}
});

ThreadGroup myGroup = new OtherGroup("OtherGroup");

new Thread(myGroup, new Runnable() {
@Override
public void run() {
int i = 1/0;
}
}, "thread1").start();

new Thread(myGroup, new Runnable() {
@Override
public void run() {
int i = 1/0;
}
}, "thread2").start();

打印结果如下:

1
2
I am a other group and do nothing
I am a other group and do nothing

如果在OtherGroup的uncaughtException加上如下代码:

1
super.uncaughtException(t,e);

那么执行结果会是:

1
2
3
4
I catch a exception from  thread1:badGroup
I am a bad group and do nothing
I catch a exception from thread2:badGroup
I am a bad group and do nothing

参考文献:
Java核心技术讲义
Java线程未捕获异常处理
《Java并发》:第五节 线程异常处理
Java多线程——<七>多线程的异常捕捉

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器