This is the sixth day of my participation in the First Challenge 2022

The background,

Have you ever wondered, when a task is added to a thread pool, the thread in the thread pool takes over and executes it. If there is an exception in the execution of the thread pool, how to handle the exception and the thread currently executing the task?

Second, the analysis

The test code

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(a) {
            System.out.println(1/0); }}); }Copy the code

Here is a brief description of the thread pool workflow (we will not analyze the variable conditions in the thread pool, etc.) :

  1. Business code is written specificallyRunnableorCallableAnd a unique interface that implements the interface.
  2. Commit the business-written implementation to the thread poolExecutorServiceIn the framework.
  3. This is calledThreadPoolExecutor#addWorkerMethods, translated into tasksWorkerAdd to the task pool. Due to theWorkerIs alsoRunnable, by calling the correspondingstartMethod to start the thread of execution.
  4. throughWorkertherunMethod that will specifically execute the corresponding implementation in Step 1runorcallMethods.

It can be seen from the simple process of the thread pool above that all tasks submitted to the thread pool will be transformed into Worker objects for specific task execution at last. Therefore, if you want to know how the thread in the thread pool is caught and processed when an exception occurs, you need to start from where the Worker is executed.

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    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);
                Throwable thrown = null;
                try {
                    // When a specific method is executed
                    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); }}finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally{ processWorkerExit(w, completedAbruptly); }}Copy the code

The try method is used to execute code that the client needs to be executed by the thread pool. Corresponding runtime and system-level exceptions are also captured. Then clean up accordingly, noting the processWorkerExit method in the outer finally. The worker. remove method is called to remove the task from the task pool. If the condition is met, addWorkder is called to create a new task.

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly)
        decrementWorkerCount();
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        // Remove the corresponding task
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if(! completedAbruptly) {int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0&&! workQueue.isEmpty()) min =1;
            if (workerCountOf(c) >= min)
                return;
        }
        addWorker(null.false); }}Copy the code

Third, summary

If a thread pool is used for the execution of specific tasks, when an exception is thrown by a thread in the thread pool, the thread pool will catch the exception and remove and destroy the Worker, and create a Worker with an empty task. In business scenarios, it is often necessary to add some information to the thread context. At this time, if an exception is thrown, the Worker will be destroyed and the corresponding thread context information page will be destroyed. Therefore, the thread context resources cannot be obtained in the newly created thread.