前言
今天遇到了一个bug,现象是,一个任务放入线程池中,似乎“没有被执行”,日志也没有打。
经过本地代码调试之后,发现在任务逻辑的前半段,抛出了NPE
,但是代码外层没有try-catch
,导致这个异常被吃掉。
这个问题解决起来是很简单的,外层加个try-catch
就好了,但是这个异常如果没有被catch,线程池内部逻辑是怎么处理这个异常的呢?这个异常最后会跑到哪里呢?
带着疑问和好奇心,我研究了一下线程池那一块的源码,并且做了以下的总结。
源码分析
项目中出问题的代码差不多就是下面这个样子
ExecutorService threadPool = Executors.newFixedThreadPool(3);
threadPool.submit(() -> {
String pennyStr = null;
Double penny = Double.valueOf(pennyStr);
...
})
先进到newFixedThreadPool
这个工厂方法中看生成的具体实现类,发现是ThreadPoolExecutor
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
再看这个类的继承关系,
再进到submit
方法,这个方法在ExecutorService
接口中约定,其实是在AbstractExectorService
中实现,ThreadPoolExecutor
并没有override这个方法。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
对应的FutureTask对象的
构造方法
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // state由volatile 修饰 保证多线程下的可见性
}
对应Callable
对象的构造方法
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
对应RunnableAdapter
对象的构造方法
/**
* A callable that runs given task and returns given result
* 一个能执行所给任务并且返回结果的Callable对象
*/
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
总结上面的,newTaskFor
就是把我们提交的Runnable
对象包装成了一个Fu