原始碼分析之ThreadPoolExecutor

特立獨行的豬手發表於2017-04-07

執行緒池在多執行緒程式設計的中可謂是個利器,使用執行緒池會大大提高多執行緒的效率。原因是使用執行緒池相對於new Thread有效避免了執行緒建立和銷燬的開銷。

Java中一般來說通過Executors來建立所需要的執行緒池,如:Executors.newFixedThreadPoolExecutors.newScheduledThreadPool等等。而其中一種主要的實現就是ThreadPoolExecutorThreadPoolExecutorJ.U.C包下的,當然也是出自大神Doug Lea之手。

ThreadPoolExecutor 結構

原始碼分析之ThreadPoolExecutor
ThreadPoolExecutor類結構

如上圖所示,ThreadPoolExecutor類大致結構如上圖所示。Executor介面只有一個execute方法。ExecutorSerivce介面在Executor的基礎上提供了對任務執行的管理,如:shutdown方法。AbstractExecutorService是針對其中invokeXXXsubmit做了預設的實現。真正的execute將由ThreadPoolExecutor自身實現。

ThreadPoolExecutor還有幾個內部類:AbortPolicyDiscardPolicyDiscardOldestPolicyCallerRunsPolicy。這幾個類的作用是當執行緒池承載很多工之後,超過maximumPoolSize的數量,無法繼續接受任務的時候,提供了不同的拒絕策略。

Worker類也是ThreadPoolExecutor的內部類,實現Runnable介面,提供了工作執行緒的實現。後面會詳細分析這個類的實現。

ThreadPoolExecutor類提供了4個構造方法。其中最核心的構造方法如下所示:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }複製程式碼

引數含義如下:

引數 含義
corePoolSize 核心執行緒池大小
maximumPoolSize 最大執行緒池大小
keepAliveTime 超過corePoolSize數量的空閒執行緒最大存活時間
corePoolSize keepAliveTime 時間單位
workQueue 工作佇列
ThreadFactory 執行緒工廠
RejectedExecutionHandler 拒絕策略,預設拒絕策略:AbortPolicy

ThreadPoolExecutor 類變數

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }複製程式碼

AtomicInteger ctl

ctl是一個AtomicInteger修飾的變數,用來儲存2個變數:

  • workerCount: 執行緒池中活動執行緒的數量
  • runState:執行緒池的執行狀態

workerCount

workerCount佔用低29位存執行緒數,workerCount代表了執行緒池活動的執行緒數量,最小數量是0,最大的數量是(1 << COUNT_BITS) - 1 (536870911);

1 << COUNT_BITS :

00000000 00000000 00000000 00000001 --> 00100000 00000000 00000000 00000000

(1 << COUNT_BITS) - 100100000 00000000 00000000 00000000 --> 00011111 11111111 11111111 11111111複製程式碼
private static int workerCountOf(int c)  { return c & CAPACITY; }複製程式碼

workerCountOf方法是用從ctl中解析出workerCount的值來。由於CAPACITY高3位是000ctl的值與CAPACITY&操作的時候,高3位將被捨棄;由於CAPACITY低29位是全是1&操作會保持原值,這樣workerCount的值就從ctl中解析出來了。

runState

runState佔用高3位存執行緒狀態,共有5個值:

  • RUNNING(-536870912):接受新任務,並處理佇列任務
  • SHUTDOWN(0): 不接受新任務,但會處理佇列中的任務
  • STOP(536870912): 不接受新任務,不會處理佇列任務,中斷正在處理的任務
  • TIDYING(1073741824): 所有任務已結束,workerCount為0,執行緒過渡到TIDYING狀態,將呼叫terminated()方法
  • TERMINATED(1610612736): terminated()方法已經完成
private static int runStateOf(int c)     { return c & ~CAPACITY; }複製程式碼

runStateOf方法的作用是解析出runState的值。~CAPACITY將反轉CAPACITY的值,也就是CAPACITY的高3位全部為1,低29位全部為0。如此ctl & ~CAPACITY 就會送ctl中解析出runState的值。

worker 類

執行緒池維護的是HashSet<Worker> workers,一個由Worker物件組成的HashSet

    // 繼承AQS,實現Runnable介面
    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;

        // 處理任務的執行緒
        final Thread thread;
        // worker 傳入的任務
        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) {
                }
            }
        }
    }複製程式碼

Worker繼承與AQS,主要想利用AQS獨佔所機制,來標示執行緒是否空閒。後續的shutdown方法在執行時候,判斷執行緒是否空閒,tryLock來實現的。同樣可以看到在Worker在初始化的時候將state設定為-1,這也是為了避免worker在執行前被中斷。

Worker繼承與Runnable,當worker啟動時候就會呼叫run方法。Worker類的run方法呼叫了runWorker方法;

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        // 將state設定為0,允許中斷
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // task為空,則呼叫getTask(),從workQueue取出新的task
            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
                // 執行緒池如果是STOP狀態,要中斷當前執行緒
                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,迴圈操作
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }複製程式碼

execute 方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

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

execute主要分3個步驟:

  • 當活動執行緒小於corePoolSize的值,將會呼叫addWorker建立新的任務執行緒;
  • 如果任務可以成功被加入至workQueue,這是將再次獲取ctl的值做雙重校驗,如果執行緒池已經shutdown,將從workQueue移除並拒絕該任務。
  • 如果任務佇列加入失敗,則拒絕任務。

addWorker 方法

  private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 執行緒池狀態大於SHUTDOWN值將不再接受新的任務,建立執行緒。
            // rs == SHUTDOWN or  firstTask == null or workQueue.isEmpty() 都不再接受任務
            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;
                // CAS操作增加workerCount的值,增加成功跳出迴圈
                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
            }
        }
        // workerCount增加成功進入下面的邏輯
        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,將執行緒從執行緒池移除,將workerCount -1
                addWorkerFailed(w);
        }
        return workerStarted;
    }複製程式碼

shutdown 方法

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 設定執行緒池狀態為:SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 中斷所有任務執行緒
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }複製程式碼

呼叫執行緒池shutdown方法,將不再接受新的任務。首先會呼叫checkShutdownAccess方法檢驗是否具有執行緒shutdown的許可權,然後將執行緒池的狀態設定為SHUTDOWN,最後中斷空閒執行緒,這裡的中斷是用Thread.interrupt()實現的,所以不會影響正在執行的執行緒,正在執行的的任務將會繼續執行。

shutdownNow 方法

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 將執行緒池狀態設定為:STOP
            advanceRunState(STOP);
            // 中斷所有執行緒
            interruptWorkers();
            // 獲取佇列中尚未被執行的任務
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }複製程式碼

shutdownNow方法與shutdown方法類似,不同是shutdownNow會將執行緒池的狀態設定STOP,在中斷過程中,少了tryLock獲取鎖的操作,所以不管執行緒是否空閒都將被中斷,中斷所有執行緒,但也不會強行終止正在執行的執行緒。最後返回阻塞佇列中沒有被執行的任務list

相關文章