本文摘自:Java并发编程从入门到精通(张振华 著)
在并发编程中,run( )方法不允许thorw exception,所有的异常必须在run方法内进行处理。
在java多线程程序中,所有的线程都不允许抛出未捕获的checked exception,也就是说各个线程需要自己把自己的checked exception处理掉。这一点是通过java.lang.Runnable.run( )方法声明(因为此方法声明上没有throw exception部分)进行了约束。但是线程依然有可能抛出unchecked exception,当抛出此类异常时,线程就会终结,而对于主线程和其他线程完全不受影响,且完全感知不到某个线程抛出的异常,也就是说完全无法catch到这个异常。
因此,在thread里面,如果要处理checked exception,简单的一个try/catch块就可以了。对于这种unchecked exception,相对来说就会有点不一样。
这是就要用到Thread里面的setUncaughtExceptionHandler(UncaughtExceptionHandler),这个方法可以用来处理一些unchecked exception。setUncaughtExceptionHandler( )方法相当于一事件注册的入口。在Thread类里面的源码如下:
<span style="font-size:14px;">public void setUncaughtExceptionHandler(UncaughtExceptionHandler paramUncaughtExceptionHandler)
{
checkAccess();
this.uncaughtExceptionHandler = paramUncaughtExceptionHandler;
}
</span>
<span style="font-family:Courier New;font-size:14px;background-color: rgb(240, 240, 240);">而UncaughtExceptionHandler则是一个接口;它的声明如下:</span>
<span style="font-size:14px;">public static abstract interface UncaughtExceptionHandler
{
public anstract void uncaughtException(Thread paramThread, Throwable paramThrowable);
}
</span>
<span style="font-size:14px;">在异常发生时,我们传入的 UncaughtExceptionHandler参数的uncaughtException方法会被调用。</span>
<span style="font-size:14px;">综合前面的讨论,我们把要实现handle unchecked exception的方法的具体步骤总结如下:</span>
<span style="font-size:14px;">(1)定义一个类实现UncaughtExceptionHandler接口。在实现的方法里包含对异常处理的逻辑和步骤。</span>
<span style="font-size:14px;">(2)定义线程执行结构和逻辑。这一步和普通线程定义一样。</span>
<span style="font-size:14px;">(3)在创建和执行该子线程的方法中,在thread.start( )语句前增加一个thread.setUncaughtExceptionHandler语句来实现处理逻辑的注册</span>
<span style="font-size:14px;">
</span>
<span style="font-size:14px;">下面按照这个步骤来创建一个示例:</span>
<span style="font-size:14px;">首先是实现UncaughtExceptionHandler接口部分
</span>
<span style="font-size:14px;">package demo.thread;
import java.lang.Thread.UncaughtExceptionHandler;
public class ExceptionHandlerThreadB implements UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.printf("An exception has been captured\n");
System.out.printf("Thread: %s\n", t.getId());
System.out.printf("Exception: %s: %s\n",
e.getClass().getName(), e.getMessage());
System.out.printf("Stack Trace: \n");
e.printStackTrace(System.out);
System.out.printf("Thread status: %s\n", t.getState());
}
}
</span>
<span style="font-size:14px;">这里我们添加的异常处理逻辑很简单,只是把线程的信息和异常信息都打印出来。</span>
<span style="font-size:14px;">然后,我们定义线程的内容,这里,我们故意让线程产生一个unchecked exception:</span>
<span style="font-size:14px;">package demo.thread;
public class ThreadB implements Runnable {
public void run() {
int number0 = Integer.parseInt("TTT");
}
}
</span>
<span style="font-size:14px;">从上面的代码中我们可以看到,Integer.parseInt()里面的参数是错误的,肯定会抛出一个异常来。</span>
<span style="font-size:14px;">现在,我们再把创建线程和注册处理逻辑的部分补上来:</span>
<span style="font-size:14px;">package demo.thread;
public class ThreadMain {
public static void main(String[] args) {
ThreadB task = new ThreadB();
Thread thread = new Thread(task);
thread.setUncaughtExceptionHandler(new ExceptionHandlerThreadB());
thread.start();
}
}</span>
<span style="font-size:14px;">现在我们执行整个程序,会发现有如下的结果
</span>
<span style="font-size:14px;">An exception has been captured
Thread: 10
Exception: java.lang.NumberFormatException: For input string: "TTT"
Stack Trace:
java.lang.NumberFormatException: For input string: "TTT"
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at unit2.ThreadB1.run(ThreadB1.java:5)
at java.lang.Thread.run(Unknown Source)
Thread status: RUNNABLE
</span>
这部分的输出正好就是我们前面实现UncaughtExceptionHandler接口的定义。
因此,对于unchecked exception,我们也可以采用类似事件注册的机制做一定程度的处理,当然也可以不做处理放任自由,读者可以试试效果什么怎么样的,这里不再实验。
总之,Java thread里面关于异常的部分比较奇特。你不能直接在一个线程里面去抛出异常。一般在线程里碰到checked exception,推荐的做法是采用try/catch块来处理。而对于unchecked exception,比较合理的方式是注册一个实现UncaughtExceptionHandler接口的对象实例来处理。
<span style="font-size:14px;">
</span>