JDK source code - thread pool ThreadPoolExecutor

Keywords: less

Basic data structure

  • AtomicInteger ctl: the first three bits are in thread pool state, and the last 29 bits are the number of active threads
  • CAS – compareAndIncrementWorkerCount: increase the number of active threads by 1 (new threads)
  • Blocking queue - BlockingQueue workQueue: work queue (the worker queue that needs to be executed by the thread pool, the specific implementation is determined by the subclass passed in during initialization)
  • Reentrant lock: add the lock when the worker thread and other operations are added
  • HashSet workers: all worker threads
  • Condition termination: support await termination
  • volatile ThreadFactory:
  • Worker: Based on AQS framework

Initialization

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

Submit tasks

public void execute(Runnable command) {
        int c = ctl.get();
        //If the number of active threads is less than the number of core threads, add a worker thread to the input parameter and return in advance
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //The thread pool is running and the current task joins the work queue
        //Add tasks to the blocking queue. The specific implementation depends on the type of blocking queue passed in during thread pool initialization
        //For bounded queues, returns false if the queue is full
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //If the thread pool is no longer functioning properly, remove the current task from the work queue and terminate, and then throw an exception
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //If the worker thread changes to 0, add the worker thread (to prevent concurrency problems)
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //If you cannot enter the work queue and add a worker thread, you will be thrown in error
        else if (!addWorker(command, false))
            reject(command);
    }
  • Add worker thread
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c); //Thread pool status
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN && firstTask == null &&! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||  //Active thread over threshold
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //Try to add a new worker thread (CAS changes the statistics first here)
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get(); 
                if (runStateOf(c) != rs) //The number of active threads has changed, so you need to add the process again
                    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 rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        //add is different from offer. When the bounded queue is full, the operation throws an exception
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

Function

public void run() {
    runWorker(this);
}

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 pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            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);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);
        // Are workers subject to culling?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c)) //CAS deduction active threads
                return null;
            continue;
        }

        try {
            Runnable r = timed ? //Get from task queue
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

Close thread pool

//The submitted tasks will be executed, and the newly submitted tasks will not be allowed to be put into the task queue. The tasks in progress will continue to be executed, and those not executed will be interrupted
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

//Set the status of thread pool to STOP, the executing task is stopped, and the unimplemented task is returned
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);
        interruptWorkers();
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

Posted by Zergman on Sun, 01 Dec 2019 11:11:18 -0800