点击蓝色小字关注!
关注一下你就不会有bug!
先上结论:submit 不会抛出异常,不论 submit 的是 Runnable 还是 Callable;execute 会抛出异常,execute 的只能是 Runnable。
看下面的案例代码:
1public static class TestRunnable implements Runnable {
2 @Override
3 public void run() {
4 System.out.println(1 / 0);
5 System.out.println("TestRunnable!");
6 // 应当处理线程池中的线程的内部异常,避免无从得知是否发生异常
7 }
8}
9
10public static class TestCallable implements Callable<Object> {
11 @Override
12 public Object call() {
13 System.out.println(1 / 0);
14 System.out.println("TestCallable!");
15 // 应当处理线程池中的线程的内部异常,避免无从得知是否发生异常
16 return "TestCallable";
17 }
18}
19
20public static void main(String[] args) {
21 // submit不会抛出异常,不论submit的是Runnable还是Callable
22 ExecutorService pool = Executors.newFixedThreadPool(5);
23 pool.submit(new TestRunnable());
24 pool.submit(new TestCallable());
25
26 // execute会抛出异常,execute的只能是Runnable
27 ExecutorService pool = Executors.newFixedThreadPool(5);
28 pool.execute(new TestRunnable());
29}
对于如何选择 submit 个 execute 方法,可以查看源码中的注释,如下:
1/**2 * how to choose submit() or execute()3 * There is a difference concerning exception/error handling.A task queued with execute() that generates some Throwable will cause the UncaughtExceptionHandler4 * for the Thread running the task to be invoked. The default UncaughtExceptionHandler, which typically prints the Throwable stack trace to System.err, will be5 * invoked if no custom handler has been installed.On the other hand, a Throwable generated by a task queued with submit() will bind the Throwable to the Future6 * that was produced from the call to submit(). Calling get() on that Future will throw an ExecutionException with the original Throwable as its cause (accessible7 * by calling getCause() on the ExecutionException).8 */
上面的意思就是说二者最大的区别就是异常处理上,
在 e
xecute 的时候,如果你没有实现一个 handler,那么他就使用默认的 handler 来处理异常,你要是实现了一个 handler 他就会使用的实例化的 handler。
除此之外,也可以改写 ThreadPoolExecutor.afterExecute() 中自定义异常。
但
是对于
submit 来说,异常是绑定到 Future 上了,但是调用 future.get() 的时候,这些异常才会给你抛出来,意味着你自己定义的 handler 其实是无效的。
如果我们
关心线程池执行的结果,则需要使用 submit 来提交task,
那么在 afterExecute 中对异常的处理也需要通过 Future 接口调用 get 方法去取结果才能拿到异常,如果我们不关心这个任务的结果,可以直接使用 ExecutorService 中的 execute 方法
(实际是继承 Executor 接口)来直接去执行任务,这样的话,
我们的 Runnable 没有经过多余的封装,在 runWorker 中得到的异常也直接能在 afterExecute 中捕捉
。
线程池中工作线程的异常,不会影响线程池里面其他线程的正常执行。线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。
好了,以上就是对线程池异常捕捉的一个记录。
▼往期精彩回顾▼面试官: 你来来谈谈java为什么要引入异常机制?推荐:浅谈工程师思维、工程师素养!点击左下角阅读原文查看历史经典技术问题汇总,右下角素质三连呀~