java线程的异常无法捕获异常_捕获Java线程池执行任务抛出的异常 futrue获取结果异常情况...

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(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()),那么此异常将被线程池吃掉。

Future submit(Callable task);

Future> submit(Runnable task);

而java.util.concurrent.ScheduledThreadPoolExecutor是继承ThreadPoolExecutor的,因此情况类似。

结论,通过覆盖ThreadPoolExecutor.afterExecute 方法,我们才能捕获到任务的异常(RuntimeException)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值