线程异常处理

一、线程池异常处理流程

1.1 当执行方式是 execute 时,可以看到堆栈异常的输出

原因:ThreadPoolExecutor#runWorker 方法中,task#run,即执行我们的方法,如果异常的话会throw x; 所以可以看到异常

1.2 当执行方式是 submit 时,堆栈异常没有输出。但是调用 Future#get 方法时,可以捕获到异常

原因:ThreadPoolExecutor#runWorker 方法中,task#run,其实还会继续执行 FutureTask#run 方法,再在此方法中 c#call 调用我们的方法,
如果报错时 setException,并没有抛出异常。当我们去 get 时,会将异常抛出

1.3 不会影响线程池里面其他线程的正常执行

1.4 线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。当线程异常,会调用 ThreadPoolExecutor#runWorker 方法最后面的 finally 中的processWorkerExit,会将此线程 remove,并重新 addworker 一个线程

二、源码执行流程

2.1 execute 执行流程

  1. 开始执行任务,新增或者获取一个线程去执行任务(比如刚开始是新增 coreThread去执行任务)。执行到 task#run 时会去执行提交的任务,如果任务执行失败,或 throw x 抛出异常
  2. 之后会到 finally 中的 afterExecute 扩展方法,我们可以扩展该方法对异常做些什么
  3. 之后因为线程执行异常会跳出 runWorker 的外层循环,进入到 processWorkerExit 方法,此方法会将执行任务失败的线程删除,并新增一个线程
  4. 之后会到 ThreadGroup#uncaughtException 方法,进行异常处理
  5. 如果没有通过 setUncaughtExceptionHandler 方法设置默认的 UncaughtExceptionHandler,就会在 uncaughtException 方法中打印出异常信息

2.2 submit 执行流程

  1. 将传进来的任务封装成 FutureTask,同样走 execute 的方法调用,然后直接返回 FutureTask
  2. 开始执行任务,新增或者获取一个线程去执行任务(比如刚开始是新增coreThread去执行任务)
  3. 执行到 task#run 时,因为是 FutureTask,所以会去调用 FutureTask#run
  4. 在 FutureTask#run 中,c#call 执行提交的任务。如果抛出异常,并不会 throw x,而是执行 setException 方法保存异常
  5. 当我们阻塞获取 submit 方法结果时(通过 FutureTask#get),才会将异常信息抛出。当然因为runWorker没有抛出异常,所以并不会删除线程

三、线程异常捕获方法

3.1 在 Runnable#run 方法中捕获代码异常

private static class ExecuteTask implements Runnable {
    
    private Integer count = 0;
    
    @Override
    public String run() {
        while (true) {
            count++;
            LOGGER.info("ExecuteTask count: {}", count);
            try {
                int i = 1 / 0;
            } catch (Exception e) {
                LOGGER.info("exception occur, message:{}, stackTrace:{}", e.getMessage(), e.getStackTrace());
            }
        }
    }
}

3.2 在 Callable#call 方法中捕获异常

private static class SubmitTask implements Callable {

    private Integer count = 0;
    
    @Override
    public String call() {
        while (true) {
            count++;
            LOGGER.info("SubmitTask count: {}", count);
            try {
                int i = 1 / 0;
            } catch (Exception e) {
                LOGGER.info("exception occur, message:{}, stackTrace:{}", e.getMessage(), e.getStackTrace());
            }
        }
    }
}

3.3 重写 ThreadPoolExecutor#afterExecute

/**
 * Method invoked upon completion of execution of the given Runnable.
 * This method is invoked by the thread that executed the task. If
 * non-null, the Throwable is the uncaught {@code RuntimeException}
 * or {@code Error} that caused execution to terminate abruptly.
 *
 * <p>This implementation does nothing, but may be customized in
 * subclasses. Note: To properly nest multiple overridings, subclasses
 * should generally invoke {@code super.afterExecute} at the
 * beginning of this method.
 *
 * <p><b>Note:</b> When actions are enclosed in tasks (such as
 * {@link FutureTask}) either explicitly or via methods such as
 * {@code submit}, these task objects catch and maintain
 * computational exceptions, and so they do not cause abrupt
 * termination, and the internal exceptions are <em>not</em>
 * passed to this method. If you would like to trap both kinds of
 * failures in this method, you can further probe for such cases,
 * as in this sample subclass that prints either the direct cause
 * or the underlying exception if a task has been aborted:
 *
 *  <pre> {@code
 * class ExtendedExecutor extends ThreadPoolExecutor {
 *   // ...
 *   protected void afterExecute(Runnable r, Throwable t) {
 *     super.afterExecute(r, t);
 *     if (t == null && r instanceof Future<?>) {
 *       try {
 *         Object result = ((Future<?>) r).get();
 *       } catch (CancellationException ce) {
 *           t = ce;
 *       } catch (ExecutionException ee) {
 *           t = ee.getCause();
 *       } catch (InterruptedException ie) {
 *           Thread.currentThread().interrupt(); // ignore/reset
 *       }
 *     }
 *     if (t != null)
 *       System.out.println(t);
 *   }
 * }}</pre>
 *
 * @param r the runnable that has completed
 * @param t the exception that caused termination, or null if
 * execution completed normally
 */
protected void afterExecute(Runnable r, Throwable t) { }

3.4 为工作者线程设置 UncaughtExceptionHandler

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值