代码
| public static void main(String[] args) { |
| final ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,new LinkedBlockingQueue<>()); |
| |
| Future<?> future = executorService.submit(() -> { |
| int a= 1/0; |
| }); |
| |
| System.out.println("hello world"); |
| } |
output
这个代码很简单, 使用ThreadPoolExecutor实例submit方法执行任务,如果代码中抛出异常,就会被吞掉。
为什么?
结论:
因为submit()
返回的是Future对象,你没有使用get()
取结果,你就拿不到任何结果,包括异常。
这点其实和C#中的Task有些类似,这是微软官方的例子:
https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/exception-handling-task-parallel-library
| public class Program |
| { |
| public class CustomException : Exception |
| { |
| public CustomException(String message) : base(message) |
| { } |
| } |
| |
| public static void Main(string[] args) |
| { |
| var task1 = Task.Run(() => { throw new CustomException("This exception is expected!"); }); |
| |
| try |
| { |
| |
| } |
| catch (AggregateException ae) |
| { |
| foreach (var e in ae.InnerExceptions) |
| { |
| |
| if (e is CustomException) |
| { |
| Console.WriteLine(e.Message); |
| } |
| |
| else |
| { |
| throw e; |
| } |
| } |
| } |
| |
| Console.ReadKey(); |
| } |
| } |
如果没有task1.Wait()
,异常是不会抛出的。
内部原理
我们使用VS Code查看源代码,一路下去看看:
- task就是我们自己定义的任务,使用newTaskFor包装下
| |
| |
| |
| |
| public Future<?> submit(Runnable task) { |
| if (task == null) throw new NullPointerException(); |
| RunnableFuture<Void> ftask = newTaskFor(task, null); |
| execute(ftask); |
| return ftask; |
| } |
- 包装了啥?哦,FutureTask
| /** |
| protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { |
| return new FutureTask<T>(runnable, value); |
| } |
把我们的任务传入FutureTask,作为callbale属性
| public FutureTask(Runnable runnable, V result) { |
| this.callable = Executors.callable(runnable, result); |
| this.state = NEW; |
| } |
- 我们看 execute(ftask)
| public void execute(Runnable command) { |
| if (command == null) |
| throw new NullPointerException(); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| int c = ctl.get(); |
| if (workerCountOf(c) < corePoolSize) { |
| |
| if (addWorker(command, true)) |
| return; |
| c = ctl.get(); |
| } |
| if (isRunning(c) && workQueue.offer(command)) { |
| int recheck = ctl.get(); |
| if (! isRunning(recheck) && remove(command)) |
| reject(command); |
| else if (workerCountOf(recheck) == 0) |
| addWorker(null, false); |
| } |
| else if (!addWorker(command, false)) |
| reject(command); |
| } |
firstTask就是刚才包装的FutureTask,实例化Worker的时候将FutureTask传入,存入
Worker实例持有一个thread对象,在worker被add之后start
| private boolean addWorker(Runnable firstTask, boolean core) { |
| retry: |
| for (int c = ctl.get();;) { |
| |
| if (runStateAtLeast(c, SHUTDOWN) |
| && (runStateAtLeast(c, STOP) |
| || firstTask != null |
| || workQueue.isEmpty())) |
| return false; |
| |
| for (;;) { |
| if (workerCountOf(c) |
| >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) |
| return false; |
| if (compareAndIncrementWorkerCount(c)) |
| break retry; |
| c = ctl.get(); |
| if (runStateAtLeast(c, SHUTDOWN)) |
| continue retry; |
| |
| } |
| } |
| |
| boolean workerStarted = false; |
| boolean workerAdded = false; |
| Worker w = null; |
| try { |
| w = new Worker(firstTask); |
| final Thread t = w.thread; |
| if (t != null) { |
| final ReentrantLock mainLock = this.mainLock; |
| mainLock.lock(); |
| try { |
| |
| |
| |
| int c = ctl.get(); |
| |
| if (isRunning(c) || |
| (runStateLessThan(c, STOP) && firstTask == null)) { |
| if (t.getState() != Thread.State.NEW) |
| throw new IllegalThreadStateException(); |
| workers.add(w); |
| workerAdded = true; |
| int s = workers.size(); |
| if (s > largestPoolSize) |
| largestPoolSize = s; |
| } |
| } finally { |
| mainLock.unlock(); |
| } |
| if (workerAdded) { |
| t.start(); |
| workerStarted = true; |
| } |
| } |
| } finally { |
| if (! workerStarted) |
| addWorkerFailed(w); |
| } |
| return workerStarted; |
| } |
- 看下Worker类
| Worker(Runnable firstTask) { |
| setState(-1); |
| this.firstTask = firstTask; |
| this.thread = getThreadFactory().newThread(this); |
| } |
Worker类实现Runnable,所以thread.start()后,会执行run方法
- 看下Worker的run方法
| public void run() { |
| runWorker(this); |
| } |
我们的FutureTask一路被整在了这里,赋值给task, task.run()执行的就是FutureTask的run方法
| final void runWorker(Worker w) { |
| Thread wt = Thread.currentThread(); |
| Runnable task = w.firstTask; |
| w.firstTask = null; |
| w.unlock(); |
| boolean completedAbruptly = true; |
| try { |
| while (task != null || (task = getTask()) != null) { |
| w.lock(); |
| |
| |
| |
| |
| if ((runStateAtLeast(ctl.get(), STOP) || |
| (Thread.interrupted() && |
| runStateAtLeast(ctl.get(), STOP))) && |
| !wt.isInterrupted()) |
| wt.interrupt(); |
| try { |
| beforeExecute(wt, task); |
| try { |
| task.run(); |
| afterExecute(task, null); |
| } catch (Throwable ex) { |
| afterExecute(task, ex); |
| throw ex; |
| } |
| } finally { |
| task = null; |
| w.completedTasks++; |
| w.unlock(); |
| } |
| } |
| completedAbruptly = false; |
| } finally { |
| processWorkerExit(w, completedAbruptly); |
| } |
| } |
- 看下FutureTask的run方法
| public void run() { |
| if (state != NEW || |
| !RUNNER.compareAndSet(this, null, Thread.currentThread())) |
| return; |
| try { |
| Callable<V> c = callable; |
| if (c != null && state == NEW) { |
| V result; |
| boolean ran; |
| try { |
| result = c.call(); |
| ran = true; |
| } catch (Throwable ex) { |
| result = null; |
| ran = false; |
| setException(ex); |
| } |
| if (ran) |
| set(result); |
| } |
| } finally { |
| |
| |
| runner = null; |
| |
| |
| int s = state; |
| if (s >= INTERRUPTING) |
| handlePossibleCancellationInterrupt(s); |
| } |
| } |
刚第2步时候说了,我们自定义的任务赋值给了callable属性
所以c.call()就是执行我们自定义的任务代码
如果发生异常,我们看到catch代码块中执行了setException(ex)
| protected void setException(Throwable t) { |
| if (STATE.compareAndSet(this, NEW, COMPLETING)) { |
| outcome = t; |
| STATE.setRelease(this, EXCEPTIONAL); |
| finishCompletion(); |
| } |
| } |
最终把异常存入了outcome
什么时候才会使用outcome?report方法
| @SuppressWarnings("unchecked") |
| private V report(int s) throws ExecutionException { |
| Object x = outcome; |
| if (s == NORMAL) |
| return (V)x; |
| if (s >= CANCELLED) |
| throw new CancellationException(); |
| throw new ExecutionException((Throwable)x); |
| } |
没错,就是get()方法调用report()
| public V get() throws InterruptedException, ExecutionException { |
| int s = state; |
| if (s <= COMPLETING) |
| s = awaitDone(false, 0L); |
| return report(s); |
| } |
稍微退后一点,如果我们的代码没有异常,就会把执行结果也放入这个outcome中
| protected void set(V v) { |
| if (STATE.compareAndSet(this, NEW, COMPLETING)) { |
| outcome = v; |
| STATE.setRelease(this, NORMAL); |
| finishCompletion(); |
| } |
| } |
优雅处理异常
回到文章最开始,因为我们执行任务并没有(不关心)返回值,所以不会调用get()方法,任务出现问题也会装的静悄悄,我们连日志都没有写。
所以简单的办法就是给我们的任务加一个try catch代码块,好歹写个日志。
那么更优雅的方式是利用现有的原理去扩展,我们回到第5步,也就是FutureTask实例执行的时候:
| try { |
| beforeExecute(wt, task); |
| try { |
| task.run(); |
| afterExecute(task, null); |
| } catch (Throwable ex) { |
| afterExecute(task, ex); |
| throw ex; |
| } |
| } |
看到没,beforeExecute,afterExecute,有点回调的意思
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected void afterExecute(Runnable r, Throwable t) { } |
瞧瞧人家的注释...
最终代码
| public class MonitoringThreadPoolExecutor extends ThreadPoolExecutor { |
| |
| public MonitoringThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { |
| super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); |
| } |
| |
| @Override |
| protected void afterExecute(Runnable r, Throwable t) { |
| if (r instanceof Future<?>) { |
| super.afterExecute(r, t); |
| if (t == null |
| && r instanceof Future<?> |
| && ((Future<?>)r).isDone()) { |
| try { |
| Object result = ((Future<?>) r).get(); |
| } catch (CancellationException ce) { |
| t = ce; |
| } catch (ExecutionException ee) { |
| t = ee.getCause(); |
| } catch (InterruptedException ie) { |
| |
| Thread.currentThread().interrupt(); |
| } |
| } |
| if (t != null) |
| System.out.println(t.getMessage()); |
| } |
| } |
| } |
| public static void main(String[] args) { |
| final ExecutorService executorService = new MonitoringThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,new LinkedBlockingQueue<>()); |
| executorService.submit(() -> { |
| int a= 1/0; |
| }); |
| |
| System.out.println("hello world"); |
| } |
output
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~