线程池异常处理

1,908 阅读3分钟

这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战

一、背景

​ 可曾想过,当将一个线程任务添加到线程池中后,由线程池中的线程接管并进行相应的执行处理,如果出现执行异常(执行任务业务逻辑是抛出异常)时线程池是如何进行异常的处理以及当前执行任务的线程如何进行处理?

二、分析

测试代码

public static void main(String[] args) {
    ExecutorService execute = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

    execute.execute(new Runnable() {
        @Override
        public void run() {
            System.out.println(1/0);
        }
    });
}

这里简单介绍一下线程池的工作流程(这里不分析线程池中的各种变量条件等):

  1. 业务代码编写具体的Runnable或者Callable的实现接口并实现该接口的唯一接口。
  2. 将业务编写的实现类通过execute等方法,提交到线程池ExecutorService框架中。
  3. 这时线程池框架会调用到ThreadPoolExecutor#addWorker方法,转化为任务Worker添加到任务池中。由于Worker也是Runnable的实现类,所以通过调用它对应的start方法启动执行该线程。
  4. 通过Workerrun方法,将具体执行对应的第1步中实现的run或者call方法中的业务逻辑。

从以上线程池的简单流程中可以看出所有提交给线程池的任务,最后都会转化为Worker的对象进行具体的任务执行。所以如果想知道线程池中的线程出现异常是怎么捕获处理的,就需要从Worker被执行处入手。

final void runWorker(Worker w) {
    // ...
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 具体方法的执行时机
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

查看对应的代码得知:这里通过try方法来执行具体客户端需要线程池执行的代码。并且捕获了相应的运行时异常和系统级异常。然后进行相应的清理工作,这里需注意这个外层finally中的processWorkerExit方法。这里会调用workers.remove方法将任务从任务池中移除。在满足条件的情况下,会调用addWorkder去创建一个新任务。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // ...
    try {
        completedTaskCount += w.completedTasks;
        // 移除对应任务
        workers.remove(w);
    }
}

三、总结

​ 如果使用线程池进行具体任务的执行,当线程池内线程出现异常抛出时,线程池会捕获改异常并将该Worker移除销毁,并创建一个空任务的Worker。在业务场景中往往可能需要将部分信息添加到线程上下文中,这时如果执行抛出异常,则改Worker被销毁,对应的线程上下文信息页会被销毁,在新创建的线程中是获取不到对应的线程上下文资源。