由于线程的本质特性,使得你不能捕获从线程中逃逸的异常。一旦异常逃出任务的run()方法它就会向外传播到控制台,除非你采取特殊的步骤捕获这种错误的异常。在Java SE5之前,你可以使用线程组来捕捉这种异常,但是有了Java SE5,就可以用Executor来解决这个问题了。
下面的任务总是会抛出一个异常,该异常会传播到其run()方法的外部,并且main()展示了当你运行它时所发生的事情:
1
2
3
4
5
6
7
8
9
10
11
12
|
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
public
class
ExceptionThread
implements
Runnable {
public
void
run() {
throw
new
RuntimeException();
}
public
static
void
main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
service.execute(
new
ExceptionThread());
}
}
|
输出如下:
1
2
3
4
5
|
Exception in thread
"pool-1-thread-1"
java.lang.RuntimeException
at com.abc.thread.ExceptionThread.run(ExceptionThread.java:
6
)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:
1145
)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
615
)
at java.lang.Thread.run(Thread.java:
745
)
|
将main的主体放在try-catch语句块中也是没有作用的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
public
class
ExceptionThread
implements
Runnable {
public
void
run() {
throw
new
RuntimeException();
}
public
static
void
main(String[] args) {
try
{
ExecutorService service = Executors.newCachedThreadPool();
service.execute(
new
ExceptionThread());
}
catch
(RuntimeException e) {
System.out.println(
"Catched Runtime Exception."
);
}
}
}
|
这将产生于前面示例相同的结果:未捕获的异常。
为了解决这个问题,我们要修改Executor产生线程的方式。Thread.UncaughtExceptionHandler是Java SE5中的新接口,它允许你在每个Thread对象上都附着一个异常处理器。Thread.UncaughtExceptionHandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。为了使用它,我们创建了一个新类型的ThreadFactory,它将在每个新创建的Thread对象上附着一个Thread.UncaughtExceptionHandler。
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
|
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
java.util.concurrent.ThreadFactory;
public
class
ExceptionThread2
implements
Runnable {
public
void
run() {
throw
new
RuntimeException(
"NullPointer"
);
}
public
static
void
main(String[] args) {
ThreadFactory tFactory =
new
MyThreadFactory();
ExecutorService service = Executors.newCachedThreadPool(tFactory);
Runnable task =
new
ExceptionThread2();
service.execute(task);
}
}
class
MyUncaughtExceptionHandler
implements
Thread.UncaughtExceptionHandler {
// 处理从线程里抛出来的异常。
public
void
uncaughtException(Thread t, Throwable e) {
System.out.println(
"Catched Throwable: "
+
e.getClass().getSimpleName() +
", "
+ e.getMessage());
}
}
class
MyThreadFactory
implements
ThreadFactory {
// 重新组织创建线程的方式
public
Thread newThread(Runnable r) {
Thread t =
new
Thread(r);
// 为每一个线程都绑定一个异常处理器。
t.setUncaughtExceptionHandler(
new
MyUncaughtExceptionHandler());
System.out.println(
"Thread["
+ t.getName() +
"] created."
);
return
t;
}
}
|
执行的结果如下:
可以看到,线程池中有2个线程,当一个线程发生异常时,该异常被捕捉了。
上面的示例使得你可以按照具体情况(在newThread()方法中使用if, case等语句)为每个线程逐个的设置处理器。如果你知道将要在代码中处处使用相同的异常处理器,那么更简单的方式是在Thread类中设置一个静态域,并将这个处理器设置为默认的处理器即可:
1
2
3
4
5
6
7
8
9
10
11
|
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
public
class
SettingDefaultHandler {
public
static
void
main(String[] args) {
// 为线程设置默认的异常处理器。
Thread.setDefaultUncaughtExceptionHandler(
new
MyUncaughtExceptionHandler());
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(
new
ExceptionThread2());
}
}
|
这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用。系统会检查线程专有版本,如果没有发现,则检查线程组是否有专有的uncaughtException()方法,如果也没有,才会调用defaultUncaughtExceptionHandler。