java中线程执行的任务接口java.lang.runnable 要求不抛出checked异常,
public interface runnable {
public abstract void run();
}
那么如果 run() 方法中抛出了runtimeexception,将会怎么处理了?
通常java.lang.thread对象运行设置一个默认的异常处理方法:
java.lang.thread.setdefaultuncaughtexceptionhandler(uncaughtexceptionhandler)
而这个默认的静态全局的异常捕获方法时输出堆栈。
当然,我们可以覆盖此默认实现,只需要一个自定义的java.lang.thread.uncaughtexceptionhandler接口实现即可。
public interface uncaughtexceptionhandler {
void uncaughtexception(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.
*
* @return the computed result
* @throws cancellationexception if the computation was cancelled
* @throws executionexception if the computation threw an exception
* @throws interruptedexception if the current thread was interrupted while waiting
*/
v get() throws interruptedexception, 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 = new threadpoolexecutor(11, 100, 1, timeunit.minutes, //
new arrayblockingqueue<runnable>(10000),//
new defaultthreadfactory()) {
protected void afterexecute(runnable r, throwable t) {
super.afterexecute(r, t);
printexception(r, t);
}
};
private static void printexception(runnable r, throwable t) {
if (t == null && r instanceof future) {
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() */
private throwable exception;
当我们获取异步执行的结果时, java.util.concurrent.futuretask.get()
public v get() throws interruptedexception, executionexception {
return sync.innerget();
}
java.util.concurrent.futuretask.sync.innerget()
v innerget() throws interruptedexception, executionexception {
acquiresharedinterruptibly(0);
if (getstate() == cancelled)
throw new cancellationexception();
if (exception != null)
throw new executionexception(exception);
return result;
}
异常就会被包装成executionexception异常抛出。
也就是说当我们想线程池 threadpoolexecutor(java.util.concurrent.executorservice)提交任务时, 如果不理会任务结果(feture.get()),那么此异常将被线程池吃掉。
<t> future<t> submit(callable<t> task);
future submit(runnable task);
而java.util.concurrent.scheduledthreadpoolexecutor是继承threadpoolexecutor的,因此情况类似。
结论,通过覆盖threadpoolexecutor.afterexecute 方法,我们才能捕获到任务的异常(runtimeexception)。
原文地址:
©2009-2014 imxylz
|求贤若渴