提交到线程池的异常被吃了

如果我们是使用submit提交的任务,那么就要注意异常的处理了。

先来看一段代码:

ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> {
    System.out.println("任务开始执行...");
    int a = 10 / 0;	// 抛出异常
});
executorService.shutdown();

很明显,我们提交的任务会抛出异常,但实际上,控制台并不会打印任何异常的信息。那异常到底被谁吃了?

那我们就需要看看我们提交任务后,线程池做了什么手脚了,在AbstractExecutorService类中:

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);	//该方法会返回一个FutureTask
    // 狸猫换太子了,execute执行的任务是处理过的
    execute(ftask);
    return ftask;
}

// 实际上就是对我们的任务又进一步封装了
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

可以看到,我们提交的task会被封装成一个FutureTask,最后交给线程池执行的实际上是这个FutureTask。FutureTask的结构如下:
在这里插入图片描述
可以看出FutureTask也实现了Runnable接口,所以到时候FutureTask的run()方法就会被调用:

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     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);		//将异常信息塞到outcome中
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;	// 将异常信息设置到outcome中
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

看到没有,FutureTask使用try-catch捕获了任务执行的异常,并通过setException方法将异常塞到了outcome中,因此我们的异常被吃了,破案了。

那怎么拿到我们的异常信息?FutureTask的get方法会返回outcome,因此调用FutureTask的get方法就可以拿到异常信息。

ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<Integer> future = executorService.submit(() -> {
    System.out.println("任务开始执行...");
    int a = 10 / 0;
    return a;
});
future.get();	//调用get方法
System.out.println("任务执行结束...");
executorService.shutdown();

在这里插入图片描述
当然,知道了异常是怎么被吃掉的,我们就可以自己捕捉异常。

ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> {
	// 自己又try-catch捕获异常
    try {
        System.out.println("任务开始执行...");
        int a = 10 / 0;
        return a;
    }catch (Exception e){
        System.out.println(e);
    }
    return null;
});

对于execute()方式执行的任务,虽然异常不会被吃掉,但依旧推荐我们自己try-catch做一下异常处理

### Java 线程池异常处理最佳实践 在 Java 中,线程池的任务执行过程中可能会抛出未捕获的异常。如果这些异常没有被妥善处理,可能导致任务中断甚至整个程序崩溃。以下是关于如何在线程池中捕获并处理异常的一些最佳实践。 #### 使用 `try-catch` 块包裹任务逻辑 最简单的方式是在提交线程池的任务内部使用 `try-catch` 来捕获可能发生的异常。这种方式适用于简单的场景,能够确保即使发生异常也不会影响其他任务的正常运行[^2]。 ```java executor.execute(() -> { try { // 执行任务逻辑 } catch (Exception e) { // 记录日志或者采取补救措施 System.err.println("Task execution failed: " + e.getMessage()); } }); ``` #### 设置自定义的 `UncaughtExceptionHandler` 对于更复杂的场景,可以通过为线程设置自定义的 `UncaughtExceptionHandler` 来统一管理未捕获的异常。这允许开发者集中化地记录错误信息或触发特定的操作[^3]。 ```java ThreadFactory threadFactory = r -> { Thread t = new Thread(r); t.setUncaughtExceptionHandler((thread, throwable) -> { System.err.printf("An exception was thrown by %s:%n", thread.getName()); throwable.printStackTrace(); }); return t; }; ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, 10, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), threadFactory, new ThreadPoolExecutor.CallerRunsPolicy() ); ``` #### 提交返回结果的任务并通过 `Future` 获取异常 当使用 `submit()` 方法代替 `execute()` 时,可以获取一个 `Future` 对象来监控任务的状态以及任何潜在的异常。调用 `get()` 方法会抛出 `ExecutionException`,其原因通常是任务中的异常[^4]。 ```java Future<?> future = executor.submit(() -> { // 可能抛出异常的任务逻辑 }); try { future.get(); // 如果任务中有异常,则在此处被捕获 } catch (InterruptedException | ExecutionException e) { Throwable cause = e.getCause(); if (cause != null) { System.err.println("Caught an exception during task execution: " + cause.getMessage()); } } ``` #### 考虑全局异常处理器的设计模式 为了进一步增强可维护性和一致性,建议引入 AOP 或者类似的框架机制,在更高层次上拦截和处理所有线程池内的异常情况。这种方法尤其适合大型项目环境下的需求[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值