如果在提交给线程池的任务中抛出了异常,这个异常可能不会打印任何内容,也没有被任何人捕获,形成一个幽灵异常。
为了避免这种危险情况的发生,有以下解决方式
- 使用Future与Callable
- 对于Runnable,使用execute方法而非submit方法
以上两种方式可以获得异常在哪里抛出,但是仍然丢失了一个重要信息:任务的具体提交位置。
如果想进一步获取上述信息,需要自行扩展ThreadPoolExecutor,使其在调度任务之前,先保存一下提交任务线程的堆栈信息。
扩展:
execute与submit的异同
execute是Executors的接口,submit则是ExecutorService的接口
对于ThreadPoolExecutor而言,其实现了execute,而sumit方法则继承自抽象类AbstractExecutorService
对于submit方法
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask); //AbstractExecutorService未实现execute
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
可以看到,submit事实上封装了一个Future。
因此,对于Runnable而言,应当直接使用execute方法,无需经由submit包装。对于希望获得返回值的Callable,则适合使用submit方法。
参考:
Java 8 源码
《实战Java高并发程序设计》
https://blog.csdn.net/hayre/article/details/53314599
网友评论