Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常,publicinterfaceRunnable {publicabstractvoidrun();
}
那么如果 run() 方法中抛出了RuntimeException,将会怎么处理了?
通常java.lang.Thread对象运行设置一个默认的异常处理方法:java.lang.Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)
而这个默认的静态全局的异常捕获方法时输出堆栈。
当然,我们可以覆盖此默认实现,只需要一个自定义的java.lang.Thread.UncaughtExceptionHandler接口实现即可。publicinterfaceUncaughtExceptionHandler {voiduncaughtException(Thread t, Throwable e);
}
而在线程池中却比较特殊。默认情况下,线程池 java.util.concurrent.ThreadPoolExecutor 会Catch住所有异常, 当任务执行完成(java.util.concurrent.ExecutorService.submit(Callable))获取其结果 时(java.util.concurrent.Future.get())会抛出此RuntimeException。/*** Waits if necessary for the computation to complete, and then
* retrieves its result.
*
*@returnthe computed result
*@throwsCancellationException if the computation was cancelled
*@throwsExecutionException if the computation threw an exception
*@throwsInterruptedException if the current thread was interrupted while waiting*/V get()throwsInterruptedException, ExecutionException;
其中 ExecutionException 异常即是java.lang.Runnable 或者 java.util.concurrent.Callable 抛出的异常。
也就是说,线程池在执行任务时捕获了所有异常,并将此异常加入结果中。这样一来线程池中的所有线程都将无法捕获到抛出的异常。 从而无法通过设置线程的默认捕获方法拦截的错误异常。
也不同通过自定义线程来完成异常的拦截。
好在java.util.concurrent.ThreadPoolExecutor 预留了一个方法,运行在任务执行完毕进行扩展(当然也预留一个protected方法beforeExecute(Thread t, Runnable r)):protected void afterExecute(Runnable r, Throwable t) { }
此方法的默认实现为空,这样我们就可以通过继承或者覆盖ThreadPoolExecutor 来达到自定义的错误处理。
解决办法如下:ThreadPoolExecutor threadPoolExecutor=newThreadPoolExecutor(11,100,1, TimeUnit.MINUTES,//newArrayBlockingQueue(10000),//newDefaultThreadFactory()) {protectedvoidafterExecute(Runnable r, Throwable t) {super.afterExecute(r, t);
printException(r, t);
}
};privatestaticvoidprintException(Runnable r, Throwable t) {if(t==null&&rinstanceofFuture>) {try{
Future>future=(Future>) r;if(future.isDone())
future.get();
}catch(CancellationException ce) {
t=ce;
}catch(ExecutionException ee) {
t=ee.getCause();
}catch(InterruptedException ie) {
Thread.currentThread().interrupt();//ignore/reset}
}if(t!=null)
log.error(t.getMessage(), t);
}
此办法的关键在于,事实上 afterExecute 并不会总是抛出异常 Throwable t,通过查看源码得知,异常是封装在此时的Future对象中的, 而此Future对象其实是一个java.util.concurrent.FutureTask的实现,默认的run方法其实调用的 java.util.concurrent.FutureTask.Sync.innerRun()。
void innerRun() {
if (!compareAndSetState(0, RUNNING))
return;
try {
runner = Thread.currentThread();
if (getState() == RUNNING) //recheck after setting thread innerSet(callable.call());
else
releaseShared(0); //cancel } catch (Throwable ex) {
innerSetException(ex);
}
}
void innerSetException(Throwable t) {
for (;;) {
int s = getState();
if (s == RAN)
return;
if (s == CANCELLED) {
//aggressively release to set runner to null,//in case we are racing with a cancel request//that will try to interrupt runner releaseShared(0);
return;
}
if (compareAndSetState(s, RAN)) {
exception = t;
result = null;
releaseShared(0);
done();
return;
}
}
}
这里我们可以看到它吃掉了异常,将异常存储在java.util.concurrent.FutureTask.Sync的exception字段中:/**The exception to throw from get()*/privateThrowable exception;
当我们获取异步执行的结果时, java.util.concurrent.FutureTask.get()publicV get()throwsInterruptedException, ExecutionException {returnsync.innerGet();
}
java.util.concurrent.FutureTask.Sync.innerGet()V innerGet()throwsInterruptedException, ExecutionException {
acquireSharedInterruptibly(0);if(getState()==CANCELLED)thrownewCancellationException();if(exception!=null)thrownewExecutionException(exception);returnresult;
}
异常就会被包装成ExecutionException异常抛出。
也就是说当我们想线程池 ThreadPoolExecutor(java.util.concurrent.ExecutorService)提交任务时, 如果不理会任务结果(Feture.get()),那么此异常将被线程池吃掉。Futuresubmit(Callabletask);
Future>submit(Runnable task);
而java.util.concurrent.ScheduledThreadPoolExecutor是继承ThreadPoolExecutor的,因此情况类似。©2009-2014 IMXYLZ求贤若渴