ThreadPoolExecutor原始碼分析

wang03發表於2021-11-13

前言

  在熟練掌握如何使用執行緒池之後,我們來對ThreadPoolExecutor進行原始碼分析。希望大家保持對原始碼的閱讀熱情,不僅要知其然,也要知其所以然。閱讀原始碼比較苦澀,請養成反覆研究琢磨為什麼這麼寫的精神,多推敲。衝鴨!

  其實有時候想不通的時候可以看一下英文註釋,還是作者解釋的精準

 

1 ThreadPoolExecutor類圖

 

2 ThreadPoolExecutor重要變數

2.1 ctl

  這個變數是整個類的核心,AtomicInteger保證了原子性,這個變數儲存了2個內容

  • 執行緒池的狀態
  • 所有工作執行緒的數量
// int是4個位元組,有32位,這裡的ctl前3位表示執行緒池的狀態,後29位標識工作執行緒的數量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// Integer.SIZE - 3 = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 容量 000 11111111111111111111111111111
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// 執行中狀態 111 00000000000000000000000000000 (-536870912)  括號內為十進位制的
private static final int RUNNING    = -1 << COUNT_BITS;
// 關閉狀態 000 00000000000000000000000000000   (0)
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 停止狀態 001 00000000000000000000000000000   (536870912)
private static final int STOP       =  1 << COUNT_BITS;
// 整理狀態 010 00000000000000000000000000000   (1073741824)
private static final int TIDYING    =  2 << COUNT_BITS;
// 終結狀態 011 00000000000000000000000000000   (1610612736)
private static final int TERMINATED =  3 << COUNT_BITS;
// Packing and unpacking ctl
// 先非然後位與運算子獲取執行緒池執行的狀態,也就是前3位
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 位與運算子獲取工作執行緒數量,也就是後29位
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

  執行緒池的狀態

  • RUNNING:接收任務,處理workQueue佇列裡的任務
  • SHUTDOWN:不再接收新的任務,但是處理workQueue佇列裡的任務
  • STOP:拒絕新任務並且拋棄佇列裡的任務
  • TIDYING:將要呼叫terminated方法
  • TERMINATED:終結狀態

2.2 Woker靜態內部類

  Worker實現了Runnable介面,說明可以當做一個可執行的任務。Woker也繼承了AbstractQueuedSynchronizer,說明可以實現鎖的功能,他是一個簡單的不可重入的互斥鎖,工作執行緒執行任務的時候,會先加鎖,如果想要中斷工作執行緒,需要先獲取鎖,否則無法中斷,工作執行緒執行完任務才會釋放鎖,然後接著從workQueue獲取任務繼續執行。Worker的主要作用是執行佇列的任務,並管理工作執行緒和統計一些東西。

/**
 * Class Worker mainly maintains interrupt control state for
 * threads running tasks, along with other minor bookkeeping.
 * This class opportunistically extends AbstractQueuedSynchronizer
 * to simplify acquiring and releasing a lock surrounding each
 * task execution.  This protects against interrupts that are
 * intended to wake up a worker thread waiting for a task from
 * instead interrupting a task being run.  We implement a simple
 * non-reentrant mutual exclusion lock rather than use
 * ReentrantLock because we do not want worker tasks to be able to
 * reacquire the lock when they invoke pool control methods like
 * setCorePoolSize.  Additionally, to suppress interrupts until
 * the thread actually starts running tasks, we initialize lock
 * state to a negative value, and clear it upon start (in
 * runWorker).
 */
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     */
    private static final long serialVersionUID = 6138294804551838833L;
    /** Thread this worker is running in.  Null if factory fails. */
    // 工作執行緒
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    // 第一個任務
    Runnable firstTask;
    /** Per-thread task counter */
    // 該工作執行緒已經完成任務的數量
    volatile long completedTasks;
    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        // 直到runWorker方法禁止被中斷
        setState(-1); 
        this.firstTask = firstTask;
        // 從執行緒工廠獲取執行緒,並把第一個任務給worker
        this.thread = getThreadFactory().newThread(this);
    }
    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }
    // Lock methods
    //
    // The value 0 represents the unlocked state.
    // The value 1 represents the locked state.
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }
    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

 

3 ThreadPoolExecutor重要函式

3.1 execute(Runnable command)

/**
 * Executes the given task sometime in the future.  The task
 * may execute in a new thread or in an existing pooled thread.
 *
 * If the task cannot be submitted for execution, either because this
 * executor has been shutdown or because its capacity has been reached,
 * the task is handled by the current {@code RejectedExecutionHandler}.
 *
 * @param command the task to execute
 * @throws RejectedExecutionException at discretion of
 *         {@code RejectedExecutionHandler}, if the task
 *         cannot be accepted for execution
 * @throws NullPointerException if {@code command} is null
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    // 獲取執行緒池狀態和執行緒數
    int c = ctl.get();
    // 工作執行緒數小於核心執行緒數
    if (workerCountOf(c) < corePoolSize) {
        // 新增工作執行緒
        if (addWorker(command, true))
            return;
        // 新增工作執行緒失敗則重新獲取執行緒池狀態和執行緒數
        c = ctl.get();
    }
    // 執行緒池正在執行且任務可以將任務放進佇列裡
    if (isRunning(c) && workQueue.offer(command)) {
        // 重新檢查 - 獲取執行緒池狀態和執行緒數
        int recheck = ctl.get();
        // 這裡重新檢查是為了以下2種情況
        // 1.當offer方法執行之後,執行緒池關閉了,回滾之前放入佇列的操作並拒絕任務
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 2.執行緒池裡沒有可用的消費執行緒,比如現在核心執行緒數就1個,前一個任務拋異常了
        // 那麼現在就沒有可用的消費執行緒了,所以要判斷還有沒有Worker,這步很關鍵
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 新增執行緒失敗則拒絕任務
    else if (!addWorker(command, false))
        reject(command);
}

3.2 addWorker(Runnable firstTask, boolean core)

/*
 * Methods for creating, running and cleaning up after workers
 */
/**
 * Checks if a new worker can be added with respect to current
 * pool state and the given bound (either core or maximum). If so,
 * the worker count is adjusted accordingly, and, if possible, a
 * new worker is created and started, running firstTask as its
 * first task. This method returns false if the pool is stopped or
 * eligible to shut down. It also returns false if the thread
 * factory fails to create a thread when asked.  If the thread
 * creation fails, either due to the thread factory returning
 * null, or due to an exception (typically OutOfMemoryError in
 * Thread.start()), we roll back cleanly.
 *
 * @param firstTask the task the new thread should run first (or
 * null if none). Workers are created with an initial first task
 * (in method execute()) to bypass queuing when there are fewer
 * than corePoolSize threads (in which case we always start one),
 * or when the queue is full (in which case we must bypass queue).
 * Initially idle threads are usually created via
 * prestartCoreThread or to replace other dying workers.
 *
 * @param core if true use corePoolSize as bound, else
 * maximumPoolSize. (A boolean indicator is used here rather than a
 * value to ensure reads of fresh values after checking other pool
 * state).
 * @return true if successful
 */
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    // 外迴圈
    for (;;) {
        // 獲取執行緒池狀態和執行緒數
        int c = ctl.get();
        // 執行緒池狀態
        int rs = runStateOf(c);
        // Check if queue empty only if necessary.
        // 這裡我做了一個小調整,看著舒服點,以下幾種情況會返回false
        // 1.執行緒池狀態為STOP,TIDYING,TERMINATED
        // 2.執行緒池狀態為SHUTDOWN且工作執行緒的firstTask不為空
        // 3.執行緒池狀態為SHUTDOWN且佇列為空
        if (rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()))
            return false;
        // 內迴圈
        for (;;) {
            // 獲取工作執行緒數
            int wc = workerCountOf(c);
            // 如果工作執行緒大於容量或者工作執行緒大於核心執行緒數(或者最大執行緒數)返回false
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 新增工作執行緒+1
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // 重新獲取執行緒池狀態和執行緒數
            c = ctl.get();  // Re-read ctl
            // 如果執行緒池狀態變了,那麼重新走外迴圈
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
            // 如果CAS操作失敗,那麼重新走內迴圈
        }
    }
    // 執行緒是否開始工作
    boolean workerStarted = false;
    // 執行緒是否新增到工作執行緒集合
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 利用顯式鎖加鎖新增Worker
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());
                // 如果執行緒池狀態是RUNNING或者是SHUTDOWN&&第一個任務為空
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    // 檢查這個執行緒是否處於活動狀態 - RUNNABLE或者RUNNING
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    //新增到工作執行緒集合
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 如果新增到工作執行緒集合則開始工作
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 如果執行緒沒有開始工作,那麼工作執行緒數量-1
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

3.3 runWorker(Worker w)

/**
 * Main worker run loop.  Repeatedly gets tasks from queue and
 * executes them, while coping with a number of issues:
 *
 * 1. We may start out with an initial task, in which case we
 * don't need to get the first one. Otherwise, as long as pool is
 * running, we get tasks from getTask. If it returns null then the
 * worker exits due to changed pool state or configuration
 * parameters.  Other exits result from exception throws in
 * external code, in which case completedAbruptly holds, which
 * usually leads processWorkerExit to replace this thread.
 *
 * 2. Before running any task, the lock is acquired to prevent
 * other pool interrupts while the task is executing, and then we
 * ensure that unless pool is stopping, this thread does not have
 * its interrupt set.
 *
 * 3. Each task run is preceded by a call to beforeExecute, which
 * might throw an exception, in which case we cause thread to die
 * (breaking loop with completedAbruptly true) without processing
 * the task.
 *
 * 4. Assuming beforeExecute completes normally, we run the task,
 * gathering any of its thrown exceptions to send to afterExecute.
 * We separately handle RuntimeException, Error (both of which the
 * specs guarantee that we trap) and arbitrary Throwables.
 * Because we cannot rethrow Throwables within Runnable.run, we
 * wrap them within Errors on the way out (to the thread's
 * UncaughtExceptionHandler).  Any thrown exception also
 * conservatively causes thread to die.
 *
 * 5. After task.run completes, we call afterExecute, which may
 * also throw an exception, which will also cause thread to
 * die. According to JLS Sec 14.20, this exception is the one that
 * will be in effect even if task.run throws.
 *
 * The net effect of the exception mechanics is that afterExecute
 * and the thread's UncaughtExceptionHandler have as accurate
 * information as we can provide about any problems encountered by
 * user code.
 *
 * @param w the worker
 */
final void runWorker(Worker w) {
    // 此處獲取的wt就是Worker裡的thread 
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 這裡為什麼要先unlock一下呢?到這一行程式碼為止,我們沒有進行任何的任務處理
    // Worker的建構函式中,setState(-1);這一行程式碼抑制了執行緒中斷,所以這裡需要unlock從而允許中斷
    w.unlock(); // allow interrupts
    // 是否是異常終止的標識,預設為true。有2中情況為true
    // 1.執行任務丟擲了異常
    // 2.worker被中斷
    boolean completedAbruptly = true;
    try {
        // 獲取任務,如果getTask()方法返回null,那麼隨之worker也要-1,之後有getTask()方法分析
        // 只有在等待從workQueue佇列裡獲取任務的時候才能中斷。
        // 第一次執行傳入的任務,之後從workQueue佇列裡獲取任務,如果佇列為空則等待keepAliveTime這麼久
        while (task != null || (task = getTask()) != null) {
            // 加鎖的目的在於防止在執行任務的時候,中斷當前worker
            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
            // 這個方法比較重要,當執行緒池正在關閉,確保worker被中斷
            // 有2次runStateAtLeast(ctl.get(), STOP)方法呼叫是因為double-check
            // 第2次檢查Thread.interrupted(),該方法會直接擦除執行緒的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) {
                    // 將異常包裝成Error丟擲
                    thrown = x; throw new Error(x);
                } finally {
                    // 執行任務之前的操作,如統計日誌等,子類自己實現
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                // 解鎖,一次任務的執行結束
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 結束worker的清理工作
        processWorkerExit(w, completedAbruptly);
    }
}

3.4 getTask()

/**
 * Performs blocking or timed wait for a task, depending on
 * current configuration settings, or returns null if this worker
 * must exit because of any of:
 * 1. There are more than maximumPoolSize workers (due to
 *    a call to setMaximumPoolSize).
 * 2. The pool is stopped.
 * 3. The pool is shutdown and the queue is empty.
 * 4. This worker timed out waiting for a task, and timed-out
 *    workers are subject to termination (that is,
 *    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
 *    both before and after the timed wait, and if the queue is
 *    non-empty, this worker is not the last thread in the pool.
 *
 * @return task, or null if the worker must exit, in which case
 *         workerCount is decremented
 */
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.
        // 當執行緒池狀態是STOP或者SHUTDOWN並且workQueue佇列是空的,返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);
        // timed用來判斷該工作執行緒是否有超時控制?
        // allowCoreThreadTimeOut引數是是否允許核心執行緒也有keepAliveTime這麼一個屬性
        // 核心執行緒預設是沒有超時限制
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        // 條件1:如果工作執行緒大於最大執行緒數或者超時了
        // 條件2:如果工作執行緒大於1或者workQueue佇列為空
        // 滿足以上2個條件則返回null
        if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        try {
            // 一個是阻塞方法,一個是非阻塞方法,關鍵還是看timed這個變數,見上
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

 3.5 shutdown

  執行緒池將不會再接收新的任務,將先前放在佇列中的任務執行完成。

/**
 * Initiates an orderly shutdown in which previously submitted
 * tasks are executed, but no new tasks will be accepted.
 * Invocation has no additional effect if already shut down.
 *
 * <p>This method does not wait for previously submitted tasks to
 * complete execution.  Use {@link #awaitTermination awaitTermination}
 * to do that.
 *
 * @throws SecurityException {@inheritDoc}
 */
public void shutdown() {
    // 獲取顯式鎖
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 檢查shutdown許可權
        checkShutdownAccess();
        // 將執行緒池狀態改為SHUTDOWN
        advanceRunState(SHUTDOWN);
        // 中斷空閒worker
        // 如果該執行緒正在工作,則不中斷
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    // 保證workQueue裡的剩餘任務可以執行完
    tryTerminate();
}

 

 

參考資料:

《Java concurrence in practice》

https://www.cnblogs.com/leesf456/p/5585627.html

相關文章