通過了解RejectedExecutionException來分析ThreadPoolExecutor原始碼

cmazxiaoma發表於2019-02-18

觀看本文章之前,最好看一下這篇文章熟悉下ThreadPoolExecutor基礎知識。

1.關於Java多執行緒的一些常考知識點
2.看ThreadPoolExecutor原始碼前的騷操作

講解本篇文章從下面一個例子開始,test1()和test2()方法都會丟擲RejectedExecutionException異常,ThreadPoolExecutor預設的拒絕任務策略是AbortPolicy。test1()中執行緒池中corePoolSize和maximumPoolSize都為2,阻塞佇列的長度是10,執行緒池最多能處理12個任務。當超過12個任務時,就會拒絕新的任務,丟擲RejectedExecutionException。而test2()中的任務沒有超過執行緒池的閥值,但是線上程池呼叫shutdown()後,執行緒池的狀態會變成shutdown,此時不接收新任務,但會處理正在執行的任務和在阻塞佇列中等待處理的任務。所以我們在shutdown()之後再呼叫submit(),會丟擲RejectedExecutionException異常。有了這個例子的基礎,我們再來分析原始碼,會好過一點。

/**
 * @author cmazxiaoma
 * @version V1.0
 * @Description: 分析丟擲RejectedExecutionException問題
 * @date 2018/8/16 14:35
 */
public class RejectedExecutionExceptionTest {

    public static void main(String[] args) {
//        test1();
        test2();
    }

    /**
     * 提交的任務數量超過其本身最大能處理的任務量
     */
    public static void test1() {
        CustomThreadPoolExecutor customThreadPoolExecutor =
                new CustomThreadPoolExecutor(2, 2,
                        0L,
                        TimeUnit.SECONDS,
                        new ArrayBlockingQueue<Runnable>(10));

        for (int i = 0; i < 13; i++) {
            CustomThreadPoolExecutor.CustomTask customTask
                    = new CustomThreadPoolExecutor.CustomTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(60 * 60);
                        System.out.println("執行緒" + Thread.currentThread().getName()
                                + "正在執行...");
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }, "success");

            if (i == 12) {
                // throw RejectedExectionException
                customThreadPoolExecutor.submit(customTask);
            } else {
                customThreadPoolExecutor.submit(customTask);
            }
        }
        customThreadPoolExecutor.shutdown();
    }

    /**
     * 當執行緒池shutdown()後,會中斷空閒執行緒。但是正在執行的執行緒和處於阻塞佇列等待執行的執行緒不會中斷。
     * shutdown(),不會接收新的執行緒。
     */
    public static void test2() {
        CustomThreadPoolExecutor customThreadPoolExecutor =
                new CustomThreadPoolExecutor(2, 2,
                        0L,
                        TimeUnit.SECONDS,
                        new ArrayBlockingQueue<Runnable>(10));

        for (int i = 0; i < 2; i++) {
            CustomThreadPoolExecutor.CustomTask customTask
                    = new CustomThreadPoolExecutor.CustomTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(60 * 60);
                        System.out.println("執行緒" + Thread.currentThread().getName()
                                + "正在執行...");
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }, "success");
            customThreadPoolExecutor.submit(customTask);
        }
        customThreadPoolExecutor.shutdown();

        CustomThreadPoolExecutor.CustomTask customTask
                = new CustomThreadPoolExecutor.CustomTask(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(60 * 60);
                    System.out.println("執行緒" + Thread.currentThread().getName()
                            + "正在執行...");
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }, "success");

        customThreadPoolExecutor.submit(customTask);
    }

}
複製程式碼

原始碼分析

執行緒池執行過程

關於執行緒池執行過程,我們看下面一幅圖,就能明白個大概。 1.當執行緒池中的執行緒數量小於corePoolSize,就會建立新的執行緒來處理新增的任務直至執行緒數量等於corePoolSize。

2.當執行緒池中的執行緒數量大於等於corePoolSize且阻塞佇列(workQueue)未滿,就會把新新增的任務放到阻塞佇列中。

3.當執行緒池中的執行緒數量大於等於corePoolSize且阻塞佇列滿了,就會建立執行緒來處理新增的任務直到執行緒數量等於maximumPoolSize

4.如果執行緒池的數量大於maximumPoolSize,會根據RejectedExecutionHandler策略來拒絕任務。AbortPolicy就是其中的一種拒絕任務策略。

執行緒池執行過程(圖來自於網路).png


submit

submit()相比於execute()而言,多了RunnableFuture<Void> ftask = newTaskFor(task, null);這一步,把task包裝成RunnableFuture型別的ftask。所以submit()有返回值,返回值型別是Future<?>,可以通過get()獲取執行緒執行完畢後返回的值。還可以通過isDone()isCancelled()cancel(boolean mayInterruptIfRunning)這些方法進行某些操作。比如判斷執行緒是否執行完畢、判斷執行緒是否被取消,顯式取消啟動的執行緒的操作。

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
複製程式碼

execute

執行緒池去處理被提交的任務,很明顯通過execute()方法提交的任務必須要實現Runnable介面。

我們來仔細看下execute()註釋,發現它說到:如果任務不能被成功提交得到執行,因為執行緒池已經處於shutdown狀態或者是任務數量已經達到容器上限,任務會被RejectedExecutionHandler處理進行拒絕操作。很明顯,註釋已經告訴上文丟擲RejectedExecutionException異常的答案了。有時候真的要仔細看註釋!!!多看註釋,事半功倍。

我們來看execute()中做了什麼操作。

1.獲取執行緒池的狀態,如果執行緒池中的執行緒數量小於corePoolSize,呼叫addWorker(command, true)建立新的執行緒去處理command任務。如果addWorker()返回失敗,我們再次獲取執行緒池的狀態。因為addWorker()失敗的原因可能有:執行緒池已經處於shutdown狀態不接收新的任務或者是存在併發,在workerCountOf(c) < corePoolSize這塊程式碼後,有其他的執行緒建立了worker執行緒,導致worker執行緒的數量大於等於corePoolSize

2.如果執行緒池的數量大於等於corePoolSize,且執行緒池的狀態處於RUNNING狀態,我們將任務放到阻塞佇列中。當任務成功放入阻塞佇列中,我們仍然需要一個雙重校驗的機制去判斷是否應該建立新的執行緒去處理任務。

因為會存在這些情況:有些執行緒在我們上次校驗後已經死掉、執行緒池在上次校驗後突然關閉處於shutdown狀態。考慮到這些原因,我們必須再次校驗執行緒池的狀態。如果執行緒池的狀態不處於RUNNING狀態,那麼就行回滾操作,把剛才入隊的任務移除掉,後續通過reject(command)執行拒絕任務策略。

如果執行緒池處於RUNNING狀態且執行緒池中執行緒數量等於0或者從阻塞佇列中刪除任務失敗(意味著:這個任務已經被其他執行緒處理掉了)且執行緒池中執行緒數量等於0,那麼呼叫addWorker(null, false)新建一個worker執行緒,去消費workQueue中裡面的任務

3.如果執行緒池不處於RUNNING狀態或者任務無法成功入隊(此時阻塞佇列已經滿了),此時需要建立新的執行緒擴容至maximumPoolSize。如果addWorker(command, false)返回false,那麼通過reject(command)執行拒絕任務策略。

這裡再嘮叨幾句,呼叫addWorker()有這4種傳參的方式,適用於不同場景。

1.addWorker(command, true)當執行緒池中的執行緒數量少於corePoolSize,會把command包裝成worker並且放入到workers集合中。如果執行緒池中的執行緒數量超過了corePoolSize,會返回false。

2.addWorker(command, false)當阻塞佇列滿了,同樣會把command包裝成worker並且放入到worker集合中。如果執行緒池中的執行緒數量超過了maximumPoolSize,會返回false。

3.addWorker(null, false)說明firstTask是個空任務,同樣把它包裝成worker並且放入到worker集合中。如果執行緒池中的數量超過了maximumPoolSize,會返回false。這樣firstTask為空的worker線上程執行的時候,也可以從阻塞佇列中獲取任務去處理。

4.addWorker(null, true):和上面一樣,只是執行緒池的執行緒數量限制在corePoolSize,超過也是返回false。使用它的有prestartAllCoreThreads()prestartCoreThread()這2個方法,其使用目的是預載入執行緒池中的核心執行緒。

    /**
     * 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();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
複製程式碼

addWorker

addWorker()主要是建立新的執行緒,然後執行任務。

1.首先判斷執行緒池的狀態是否滿足建立worker執行緒的要求。

如果執行緒池的狀態大於SHUTDOWN狀態,那麼此時處於STOP、TIDYING、TERMINATE狀態,不能建立worker執行緒,返回false。

如果執行緒池處於shutdown狀態且firstTask不等於null,此時也無法建立worker執行緒。因為處於shutdown狀態的執行緒池不會去接收新的任務。

如果執行緒池處於shutdown狀態且firstTask等於null且workQueue阻塞佇列為空,此時就更沒有必要建立worker執行緒了。因為firstTask為null,就是為了建立一個沒有任務的worker執行緒去阻塞佇列裡面獲取任務。而阻塞佇列都已經為空,那麼再建立一個firstTask為null的worker執行緒顯然沒有什麼意思,返回false即可。

  1. 判斷執行緒池中的執行緒數量是否超過最大值。當core為true,最大值為corePoolSize。當core為false,最大值為maximumPoolSize。如果超過最大值,也無法建立worker執行緒,直接返回false即可。如果沒有超過最大值,通過CAS操作讓當前執行緒數加1,然後通過標籤跳轉跳出迴圈體至retry:位置。如果CAS操作失敗,說明workerCount被其他執行緒修改過。我們再次獲取ctl,判斷當前執行緒池狀態和之前的狀態是否匹配。如果不匹配,說明執行緒池狀態發生變更,繼續迴圈操作。

3.通過傳入來的firstTask建立worker執行緒。Worker的構造方法中通過setState(-1)設定state(同步狀態)為-1。Worker繼承了AbstractQueuedSynchronizer,其本身是一把不可重入鎖。getThreadFactory().newThread(this)建立新執行緒,因為Worker實現了Runnable介面,其本身也是一個可執行的任務。

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
複製程式碼

4.我們往workers新增worker執行緒時,通過ReentrantLock保證執行緒安全。只有在當前執行緒池處於RUNNING狀態或者是處於SHUTDOWN狀態且firstTask等於null的情況下,才可以新增worker執行緒。如果worker執行緒已經處於啟動且未死亡的狀態,會丟擲IllegalThreadStateException異常。

新增完畢後,啟動worker執行緒。如果worker執行緒啟動成功返回true,啟動失敗呼叫addWorkerFailed()進行回滾操作。

    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }
複製程式碼
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                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
            }
        }

        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 {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        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 {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
複製程式碼

Worker

我們來看下ThreadPoolExecutor的內部類Worker,上文已經說到Worker繼承了AbstractQueuedSynchronizer類且實現了Runnable介面。所以說是一個可執行的任務,也是一把不可重入鎖,具有排他性。

1.我們建立Worker物件時,預設的state為-1。我們中斷的時候,要獲取worker物件的鎖(state從0 CAS到1)。獲取鎖成功後,才能進行中斷。這說明了在初始化worker物件階段,不允許中斷。只有呼叫了runWorker()之後,將state置為0,才能中斷。

2.shutdown()中呼叫interruptIdleWorkers()中斷空閒執行緒和shutdownNow()中呼叫interruptWorkers()中斷所有執行緒。

interruptIdleWorkers()中中斷空閒執行緒的前提是要獲取worker物件的鎖。

    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

    private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }
複製程式碼

interruptWorkers()中中斷所有執行緒時,不用呼叫tryLock()獲取worker物件的鎖,最終是通過worker中的interruptIfStarted()來中斷執行緒。在這個方法中只有state大於等於0且執行緒不等於null且執行緒沒有被中斷過,才能進行中斷操作。說明只有經過了runworker()階段才能進行中斷操作。

這也是Worker為什麼要設計成不可重入的原因,就是為了防止中斷在執行中的任務,只會中斷在等待從workQueue中通過getTask()獲取任務的執行緒(因為他們沒有上鎖,此時state為0)。

以下這5種方法都會呼叫到interruptIdleWorkers()去中斷空閒執行緒。

setCorePoolSize()
setKeepAliveTime(long time, TimeUnit unit)
setMaximumPoolSize(int maximumPoolSize)
shutdown()
allowCoreThreadTimeOut(boolean value)
複製程式碼

還有一點必須強調。Task沒有真正的被執行,執行的是Work執行緒。Work執行緒中只是呼叫到了Task中的run()方法。

    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) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            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) {
                }
            }
        }
    }
複製程式碼

runWorker

1.work執行緒啟動後,會呼叫其run()方法。run()方法再去呼叫runWorker(this)方法。

2.執行任務之前,獲取work執行緒中的task,然後釋放worker的鎖。讓state狀態從-1 CAS到0。當state為0,說明可以去中斷此執行緒。

3.以輪詢的方式通過getTask()從阻塞佇列中獲取task,當task為null,跳出輪詢。

4.開始執行任務的時候,通過lock()獲取鎖,將state從0 CAS到1。任務執行完畢時,通過unlock()釋放鎖。

5.如果執行緒池處於STOP、TIDYING、TERMINATE狀態,要中斷worker執行緒。

6.通過beforeExecute(wt, task)和afterExecute(task, thrown)對task進行前置和後置處理。

7.在task.run()、beforeExecute(wt, task)、afterExecute(task, thrown)發生異常時都會導致worker執行緒終止。通過呼叫processWorkerExit(w, completedAbruptly)來進行worker退出操作。

8.在getTask()獲取阻塞佇列中的任務,如果佇列中沒有任務或者是獲取任務超時,都會呼叫processWorkerExit(w, completedAbruptly)來進行worker退出操作。

    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);
        }
    }
複製程式碼

getTask

上文已經提起過getTask()方法,主要是從阻塞佇列獲取task的。那麼條件下task會返回null呢?我們可以通過註釋得到一些資訊。

  • 超過了maximumPoolSize設定的執行緒數量,因為呼叫了setMaximumPoolSize()方法。
  • 執行緒池處於stop狀態。
  • 執行緒池處於shutdown狀態且workQueue為空.
  • 獲取任務等待超時。

1.首先獲取執行緒池執行狀態,如果執行緒池的狀態處於shutdown狀態且workQueue為空,或者處於stop狀態。然後呼叫decrementWorkerCount()遞減workerCount,最後返回null。

     * Decrements the workerCount field of ctl. This is called only on
     * abrupt termination of a thread (see processWorkerExit). Other
     * decrements are performed within getTask.
     */
    private void decrementWorkerCount() {
        do {} while (! compareAndDecrementWorkerCount(ctl.get()));
    }
複製程式碼

2.allowCoreThreadTimeOut預設為false。為false的時候,核心執行緒即時在空閒時也會保持活躍。為true的時候,核心執行緒在keepAliveTime時間範圍內等待工作。如果執行緒池的數量超過maximumPoolSize或者等待任務超時或者workQueue為空,那麼直接通過CAS減少workerCount數量,返回null。

3.如果timed為true,通過workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)獲取task,等待時間超過了keepAliveTime還沒獲取到task,直接返回null。如果timed為false,通過workQueue.take()獲取task。如果沒有獲取到task,會一直阻塞當前執行緒直到獲取到task(當阻塞佇列中加入了新的任務,會喚醒當前執行緒)為止。

4.如果獲取task成功,就直接返回。如果獲取task超時,timedOut會置為true,會在下一次迴圈中以返回null告終。

再強調一點,只有當執行緒池中的執行緒數量大於corePoolSize才會進行獲取任務超時檢查,這也體現執行緒池中的一種策略:當執行緒池中執行緒數量達到maximumPoolSize大小後,如果一直沒有任務進來,會逐漸減少workerCount直到執行緒數量等於corePoolSize。

    /**
     * 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.
            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))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
複製程式碼

processWorkerExit

1.completedAbruptly為true,說明worker執行緒時突然終止,說明執行task.run()發生了異常,所以要通過CAS減少workerCount的數量。 2.completedAbruptly為false,說明worker執行緒是正常終止,不需要對workerCount進行減少的操作。因為在getTask()中已經做了此操作。

3.對worker完成的任務數進行統計,並且從workers集合中移出。

4.呼叫tryTerminate()方法,嘗試終止執行緒池。如果狀態滿足的話,執行緒池還存線上程,會呼叫interruptIdleWorkers(ONLY_ONE)進行中斷處理,使其進入退出流程。如果執行緒池中的執行緒數量等於0的話,通過CAS把執行緒池的狀態更新到TIDYING。然後通過terminated()進行一些結束的處理,最後通過CAS把執行緒池狀態更新到TERMINATED。最後的最後,呼叫termination.signalAll()喚醒等待的執行緒,通知它們執行緒池已經終止。

    final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        terminated();
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }
複製程式碼

5.獲取執行緒池的狀態。如果執行緒池的狀態還處於RUNNING、SHUTDOWN,說明tryTerminate()沒有成功。如果worker執行緒是突然終止的話,通過addWorker(null, false)再建立一個沒有task的worker執行緒去處理任務。

6.如果worker執行緒是正常終止的話,且當前執行緒池中的執行緒數量小於需要維護的數量,我們也會通過addWorker(null, false)再建立一個沒有task的worker執行緒去處理任務。

7.預設情況下allowCoreThreadTimeOut為false,那麼min就等於corePoolSize。那麼執行緒池需要維護的執行緒數量就是corePoolSize個。如果allowCoreThreadTimeOut為true,min就等於0。在workQueue不等於空的情況,min會被賦值成1。此時執行緒池需要維護的執行緒池數量是1。

如果執行緒池處於shutdown狀態,在workQueue不為空的情況下,執行緒池始終會維護corePoolSize個執行緒。當workQueue為空的話,執行緒池會逐漸銷燬這corePoolSize個執行緒。

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            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; // replacement not needed
            }
            addWorker(null, false);
        }
    }
複製程式碼

尾言

大家好,我是cmazxiaoma(寓意是沉夢昂志的小馬),感謝各位閱讀本文章。 小弟不才。 如果您對這篇文章有什麼意見或者錯誤需要改進的地方,歡迎與我討論。 如果您覺得還不錯的話,希望你們可以點個贊。 希望我的文章對你能有所幫助。 有什麼意見、見解或疑惑,歡迎留言討論。

最後送上:心之所向,素履以往。生如逆旅,一葦以航。

saoqi.png

相關文章