Executor(三):ThreadPoolExecutor源碼解析 jdk1.8

目錄

ThreadPoolExecutor成員變量介紹

主要方法

內部運行原理


 爲什麼要使用ThreadPoolExecutore,使用它能解決什麼問題?

在jdk文檔中給出了說明。

Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.

線程池解決兩個不同的問題:他們通常提供改進性能當執行大量的異步任務時。爲了減少每個任務的調用開銷。並且他們提供一個邊界手段和管理資源。包括線程,在執行一個集合任務的消耗。每個ThreadPoolExecutor也保持一些基本統計,比如完成任務的數量。

可以看到線程池相比單獨使用線程可以減少線程頻繁創建和銷燬所產生的開銷,並且它能使用一定的規則控制線程的數量,從而避免計算機資源耗盡。同時,它也能提供一些統計等,方便進行異步任務的耗時,日誌等統計。

但是如果不懂線程池的內部原理還容易亂用,用錯。所以在阿里的規範手冊中建議自己手動構建線程池不要使用JDK提供的工廠類Executors中提供的幾種設定好的線程池。

ThreadPoolExecutor成員變量介紹

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
//這裏減去1的目的是使CAPACITY的二進制變爲全1的形式,方便分離ctl中的運行狀態和當前的工作線程數
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; }

這裏最重要的就是ctl字段,它包含了線程池的工作狀態和線程池當前的工作線程數。高位表示線程池狀態低位表示線程池中運行的線程數。通過runStateOf獲取線程狀態,workerCountOf獲取工作線程數。

In order to pack them into one int, we limit workerCount to(2^29)-1 (about 500 million) threads rather than (2^31)-1 (2billion) otherwise representable. If this is ever an issue inthe future, the variable can be changed to be an AtomicLong,and the shift/mask constants below adjusted. But until the need arises, this code is a bit faster and simpler using an int. 

爲了將他們放到一個int變量中。我限制工作線程(2^29)-1大概500百萬線程 而不是(2^31)-1.如果這在將來成爲一個問題,變量可以改變成爲Long類型的原子變量 shift/mask常量也要做調整。但是直到需要發生,這個代碼使用int是會快一點和簡單

The workerCount is the number of workers that have been permitted to start and not
permitted to stop.  The value may be transiently different from the actual number of live
threads, for example when a ThreadFactory fails to create a thread when asked, and when exiting threads are still 
performing bookkeeping before terminating. The user-visible
pool size is reported as the current size of the workers set.
工作線程數是工作的已經允許運行並且不允許停止的線程數。這個值可能短暫的不同於真實的活躍的線程數。比
如:當一個線程工廠創建一個線程失敗當需要的時候還有當存在線程仍然在執行終止前的記賬。線程池大小使用
者可見性是返回當前工作線程集合的大小。

線程池狀態介紹
RUNNING:  Accept new tasks and process queued tasks
運行時:接受一個新的任務並且處理隊列任務。

SHUTDOWN: Don't accept new tasks, but process queued tasks
關閉:不接收新的任務,但是處理隊列中的任務

STOP: Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
停止:不接受新的任務,不處理隊列任務,並且中斷正在處理的任務

TIDYING:  All tasks have terminated, workerCount is zero,the thread transitioning to state TIDYING
will run the terminated() hook method
收拾:所有任務都終止,工作線程爲0,線程過度到TIDYING狀態將會運行terminated鉤子方法

TERMINATED: terminated() has completed
終止:terminated()方法已經執行完畢

狀態之間的轉換

  The queue used for holding tasks and handing off to workerthreads.  We do not require 
that workQueue.poll() returningnull necessarily means that workQueue.isEmpty(), so 
relysolely on isEmpty to see if the queue is empty (which we mustdo for example when 
deciding whether to transition fromSHUTDOWN to TIDYING).  This accommodates special-
purposequeues such as DelayQueues for which poll() is allowed to return null even if it may 
later return non-null when delays expire.
 隊列被用來保存任務並且傳遞給工作線程,我們不要求poll方法返回null必然意味着工作隊列是空的,因此唯一
依賴isEmpty方法去判斷對壘是否爲空(我們必須舉例,比如,當決定是否要將SHUTDOWN狀態轉換爲TIDYING)這
樣一來專門隊列比如DelayQueues的poll方法允許返回null自己它可能晚點返回非空的當延期期滿
private final BlockingQueue<Runnable> workQueue;


Lock held on access to workers set and related bookkeeping. While we could use a concurrent set of some sort, it turns out  to be generally preferable to use a lock. Among the reasons is  that this serializes interruptIdleWorkers, which avoids unnecessary interrupt storms, especially during shutdown. Otherwise exiting threads would concurrently interrupt those that have not yet interrupted. It also simplifies some of the associated statistics bookkeeping of largestPoolSize etc. We  also hold mainLock on shutdown and shutdownNow, for the sake of ensuring workers set is stable while separately checking permission to interrupt and actually interrupting.
鎖用於訪問工作者集合和關聯記錄。當我們使用某種併發集合,事實證明最好使用鎖。其中的一個原因是序列化interruptIdleWorkers,避免中斷風暴,特別是在關閉的時候。否則,退出線程那些還沒有中斷的將同時中斷。它也簡化了最大線程數關聯統計記錄等等。我們也次有mainLock在shutdown和shutdownNow方法。爲了確保工作線程集合時穩定的在分別檢查允許中斷和真實中斷中的期間
 private final ReentrantLock mainLock = new ReentrantLock();

Set containing all worker threads in pool. Accessed only when holding mainLock.
 集合包含線程池所有工作線程。存取只有在持有mainLock的時候
 private final HashSet<Worker> workers = new HashSet<Worker>();


Factory for new threads. All threads are created using this factory (via method addWorker).  All callers must be prepared  for addWorker to fail, which may reflect a system or user's policy limiting the number of threads.  Even though it is not treated as an error, failure to create threads may result in new tasks being rejected or existing ones remaining stuck in the queue.
工廠用用創建新的線程。所有線程被創建使用這個工廠。經由方法addWorker.  所有的調用者必須準備addWorker失敗的情況,可能反應一個系統或用戶的策略限制線程的數量。及時它不是作爲一個錯誤對待,創建一個線程失敗可能導致一個新的任務被拒絕或先行在隊列中的任務卡死
private volatile ThreadFactory threadFactory;

 Handler called when saturated or shutdown in execute.
 在調用時出現飽和和關閉的應對方法
 private volatile RejectedExecutionHandler handler;

Core pool size is the minimum number of workers to keep alive (and not allow to time out etc) unless allowCoreThreadTimeOut is set, in which case the minimum is zero.

核心線程池大小,除非設置了允許核心線程,那種情況下這個值允許爲0

private volatile int corePoolSize;

Maximum pool size. Note that the actual maximum is internally bounded by CAPACITY. 

允許最大的線程數

private volatile int maximumPoolSize;

 

工作類

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.
        //使用AQS構建鎖
        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) {
                }
            }
        }
    }

主要方法

嘗試終止線程池方法

 /**
     * Transitions to TERMINATED state if either (SHUTDOWN and pool
     * and queue empty) or (STOP and pool empty).  If otherwise
     * eligible to terminate but workerCount is nonzero, interrupts an
     * idle worker to ensure that shutdown signals propagate. This
     * method must be called following any action that might make
     * termination possible -- reducing worker count or removing tasks
     * from the queue during shutdown. The method is non-private to
     * allow access from ScheduledThreadPoolExecutor.
     * 轉換去TERMINATED狀態如果(SHUTDOWN並且線程池和隊列都是空的)或者
     * (STOP且線程池是空的)滿足一個。如果其他的滿足條件但是工作線程不是0,
     * 中斷一個空閒工作線程去確保關閉信號傳播。這個方法必須被調用跟隨任何行爲
     * ,可能產生終止的行爲。
     */
    final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            //如果線程池還是運行狀態或運行狀態大於TIDYING或者(運行狀態是關閉的且工作隊列不爲空
            //滿足上面中的一種都將會直接返回,
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            //如果到這裏說明工作狀態是SHUTDOWN或STOP,且如果是SHUTDOWN工作隊列也必須爲空
            //如果工作線程不爲空,則會中斷一個線程
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                //設置爲TIDYING狀態,工作線程設置爲0
                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
        }
    }

關閉線程池方法shutdown和shutdownNow對比 

/**
     * 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.
     * 這個方法不會等到先前提交的任務執行完畢,如果想這樣使用awaitTermination方法。
     *
     * @throws SecurityException {@inheritDoc}
     */
    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //設置線程池狀態
            advanceRunState(SHUTDOWN);
            //中斷線程,但是要獲取到worker的鎖
            interruptIdleWorkers();
            //執行正在關閉的鉤子方法
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        //嘗試去轉換terminate狀態
        tryTerminate();
    }
/**
     * Attempts to stop all actively executing tasks, halts the
     * processing of waiting tasks, and returns a list of the tasks
     * that were awaiting execution. These tasks are drained (removed)
     * from the task queue upon return from this method.
     * 企圖去停止所有活躍在執行的任務,停止正在等待任務的處理,並且返回一個
     * 那些等待執行的任務。這個方法返回的任務都是從工作隊列中移除的。
     * <p>This method does not wait for actively executing tasks to
     * terminate.  Use {@link #awaitTermination awaitTermination} to
     * do that.
     * 這個方法不會等待正在執行的任務停止,
     *
     * <p>There are no guarantees beyond best-effort attempts to stop
     * processing actively executing tasks.  This implementation
     * cancels tasks via {@link Thread#interrupt}, so any task that
     * fails to respond to interrupts may never terminate.
     * 這個不保證超過最大努力去通知正在執行的任務。這個實現取消任務通過中斷完成
     * 因此任務任務對中斷迴應失敗可能永遠都不會終止
     *
     * @throws SecurityException {@inheritDoc}
     */
    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;
    }

可以看到shutdown()和shutdownNow()方法大致一致,但是還是有幾個地方有區別,一個是設置線程池的狀態,還有一個是調用線程中斷的方法。

shutdown調用的方法interruptIdleWorkers

/**
     * Common form of interruptIdleWorkers, to avoid having to
     * remember what the boolean argument means.
     */
    private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
    }
/**
     * Interrupts threads that might be waiting for tasks (as
     * indicated by not being locked) so they can check for
     * termination or configuration changes. Ignores
     * SecurityExceptions (in which case some threads may remain
     * uninterrupted).
     * 中斷那些可能在等待任務的線程(表明沒有被鎖)隱藏他們能被
     * 檢測終止或設置改變。忽略SecurityExceptions在這種情況一些線程可能保持
     * 不中斷
     *
     * @param onlyOne If true, interrupt at most one worker. This is
     * called only from tryTerminate when termination is otherwise
     * enabled but there are still other workers.  In this case, at
     * most one waiting worker is interrupted to propagate shutdown
     * signals in case all threads are currently waiting.
     * Interrupting any arbitrary thread ensures that newly arriving
     * workers since shutdown began will also eventually exit.
     * To guarantee eventual termination, it suffices to always
     * interrupt only one idle worker, but shutdown() interrupts all
     * idle workers so that redundant workers exit promptly, not
     * waiting for a straggler task to finish.
     *                如果onlyOne爲true,最多會中斷一個工作線程。這是只有在
     *                tryTerminate方法中會這樣調用當終止時其他啓動但是仍然有其他工作線程
     *                在這種情況,最多一個等待的工作此案城被中斷去傳播關閉信號如果所有線程都在等待。
     *                中斷任何任意線程確保新到達工作線程自關閉開始也最終離開。
     *                去保證最終終止,它通常足夠中斷只有一個空閒線程,但是shutdown方法中斷
     *                所有空閒線程隱藏過剩的工作離開,不要等待一個流浪的任務去完成。
     */
    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();
        }
    }

shutdownNow()調用的中斷方法

/**
     * Interrupts all threads, even if active. Ignores SecurityExceptions
     * (in which case some threads may remain uninterrupted).
     * 中斷所有線程,即使是活躍的。忽略安全異常。這種情況一些線程可能保持不中斷。
     * 這裏的不需要或者工作類的鎖,但是shutdown中的方法需要獲取worker的鎖
     */
    private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }
void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }

大致上也是差不多的,區別在於shutdownNow調用的中斷方法不用去獲取worker的鎖,也就是可以任意的中斷線程,但是shutdown調用的需要去獲取worker的鎖。這點區別造成的影響就是shutdown不可能中斷正在運行任務的線程,因爲鎖被正在運行任務的方法獲取了。需要等到方法執行完再去競爭鎖。但是shutdownNow是可以直接中斷重在運行任務的線程,前提是那個任務會響應中斷。

添加工作線程方法

/**
     * 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.
     * 檢測一個新的工程線程能被添加關於當前線程池狀態和給定的核心線程數和最大線程數
     * 如果是這樣的話,工作線程數會被相應的調整,如果可能,一個新的工作線程被創建和開始
     * 運行firstTask參數做爲他的第一個任務。這個方法返回false如果線程池是停止的
     * 或者被關閉。如果線程工廠創建線程失敗也會返回false.
     * @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.
            //如果運行狀態大於等於SHUTDOWN且 不滿足 (運行狀態是SHUTDOWN且任務參數爲空且工作隊列不爲空)=》也就是如果是SHUTDOWN狀態下還有任務沒有完成
            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;
                //如果這裏工作線程數加一成功,但是後面的創建線程失敗了,會在addWorkerFailed方法中將這個數減一
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                //如果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 ||
                            //這裏這樣判斷是如果是SHTUDOWN狀態就不會接受新任務了
                        (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;
    }
/**
     * Rolls back the worker thread creation.
     * - removes worker from workers, if present
     * - decrements worker count
     * - rechecks for termination, in case the existence of this
     *   worker was holding up termination
     *
     *   回滾工作線程的創建。
     *   --移除工作線程從工作集合中,如果已經存在
     *   --減少工作線程數
     *   --再次檢查是否終止,
     */
    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

運行任務

/**
     * 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.
     * 我們可能從一個初始的任務開始。在這種情況我們不需要去獲取第一個任務
     * 另外的,線程池開始運行,我們從隊列中獲取任務,如果它返回null那麼由於改變線程池狀態或配置參數
     * 工作線程退出。其他退出結果從異常拋出外面的代碼,在這種情況completedAbruptly保留
     * 通常會引導processWorkerExit去取代這個線程。
     *
     *
     * 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.
     * 每個任務執行前都會執行beforeExecute,在這個期間可能會拋出異常,
     * 在這種情況我們將線程殺掉(跳出循環)不處理任務
     *
     * 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.
     * 4,假設beforeExecute執行正常,我們執行任務,收集它拋出的任何異常
     * 然後發送給afterExecute。我們分別處理運行時隱藏,錯誤和任意的throwable.
     * 因爲我們不能再次拋出throwalbe在run方法中,我們包裝他們在錯誤這種方法
     *
     *
     * 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.
     * 在任務執行完成,我們調用afterExecute,這個也可能拋出一個異常,
     * 也會導致線程死亡。根據JSL Sec14.20 這個異常將會生效,即使任務丟失。
     *
     * 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) {
        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、
                //如果線程池停止,確保線程中斷
                //如果不是,確保線程不是中斷,這個要求一個再次檢測在第二種情況去
                //處理shutdownNow競賽在清除中斷的期間。
                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);
        }
    }

獲取任務

 /**
     * 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:
     * 執行阻塞或超時爲一個任務,依賴當前設置或返回null如果這個工作線程必須退出
     * 因爲下面的某種情況之一:
     * 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.
     * 線程池shutdown並且任務隊列已經空了
     * 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;
            //如果線程超過了最大線程數或((設置核心線程是可以超時或線程超過了核心線程數)且下面的獲取任務超時了)
            //且(工作線程大於1或工作隊列爲空)
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    //如果返回null,runWorker方法調用processWorkerExit時如果當前工作線程大於1或工作隊列爲空則不會
                    //新建線程
                    return null;
                continue;
            }

            try {
                //線程池中線程的超時時間是通過這裏從阻塞隊列中獲取任務設置超時時間實現的。
                //當獲取一個任務超過了設置的超時時間將會返回null,當獲取任務爲null時,在
                //執行任務的方法中會做出相應的處理。
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

任務運行失敗或中斷執行的方法

有些執行失敗可能是任務拋出異常需要重新創建工作線程,有些是因爲線程池要關閉了。

/**
     * Performs cleanup and bookkeeping for a dying worker. Called
     * only from worker threads. Unless completedAbruptly is set,
     * assumes that workerCount has already been adjusted to account
     * for exit.  This method removes thread from worker set, and
     * possibly terminates the pool or replaces the worker if either
     * it exited due to user task exception or if fewer than
     * corePoolSize workers are running or queue is non-empty but
     * there are no workers.
     * 執行清除和記錄爲死亡的工作線程。只有從工作線程調用,除非completedAbruptly被設置,
     * 假設workerCount已經被調整了。這個方法將這個線程從工作線程集合中移除,並且
     * 可能終止線程池或取代工作線程如 它退出是因爲執行任務拋出議程或當前工作的線程數
     * 少於核心工作線程活隊列非空但是沒有工作線程了。
     *
     *
     * @param w the worker
     * @param completedAbruptly if the worker died due to user exception
     */
    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        //如果completedAbruptly爲false說明沒有拋出異常,任務被正常之執行了。
        //調整工作線程數
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //將當前線程的完成任務的數量加到completedTaskCount
            completedTaskCount += w.completedTasks;
            //從工作線程集合中移除
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        //嘗試去關閉
        tryTerminate();

        int c = ctl.get();
        //如果線程池運行狀態小於STOP,
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                //如果任務不是中斷的,則爲核心工作線程數。如果課中斷則爲0.
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                //核心線程是不會超時且隊列不爲空,最小值設置爲1
                //確保在覈心工作線程能超時銷燬時,如果有任務則需要至少有一個線程在處理任務
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                //這裏主要是爲了防止maximum的線程毀滅時造成線程又加一,只有核心線程毀滅時纔會加一

                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

構造方法


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

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

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

   
    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

提供了四個構造方法,參數最多的構造方法需要的參數有corePoolSize核心線程數,maximumPoolSize最大線程數,

keepAliveTime空閒時間,unit時間單位,workQueue工作任務隊列,threadFactory創建線程工廠,handler拒絕任務策略七個參數。其中任務拒絕策略,線程工廠方法有默認的設置。所以最少的參數是五個參數。

內部運行原理

核心線程數和最大線程數

ThreadPoolExecutor will automatically adjust the pool size (see getPoolSize()) according to the bounds set by corePoolSize (see getCorePoolSize()) and maximumPoolSize (see getMaximumPoolSize()). When a new task is submitted in method execute(Runnable), and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full. By setting corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool. By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks. Most typically, core and maximum pool sizes are set only upon construction, but they may also be changed dynamically using setCorePoolSize(int) and setMaximumPoolSize(int).

當一個新任務提交併且當前線程比corePoolSize小,一個新的線程將會被創建去處理請求,及時其他工作線程是
空閒的。如果當前線程數超過了corePoolSize但是小於maximumPoolSize線程,一個新的線程將會被創建只在隊
列滿的時候。通過設置corePoolSize和maximumPoolSize一樣的值,你創建一個固定大小的線程池。 通過設置
maximumPoolSize到一個本質上是無線的值比如Integer最大值,你允許池 任意的調整併發任務數。大多數情況
下,core和maximum的大小設置只在構建時,但是他們也能夠動態的改變使用setCorePoolSize和
setMaximumPoolSize

鉤子方法

This class provides protected overridable beforeExecute(Thread, Runnable) and afterExecute(Runnable, Throwable) methods that are called before and after execution of each task. These can be used to manipulate the execution environment; for example, reinitializing ThreadLocals, gathering statistics, or adding log entries. Additionally, method terminated() can be overridden to perform any special processing that needs to be done once the Executor has fully terminated.

If hook or callback methods throw exceptions, internal worker threads may in turn fail and abruptly terminate.

這個類提供了受保護的可覆蓋的beforeExecute,afterExecute方法,他們將會被調用 * 在執行每個任務之前和之後。這可用被用來操縱執行環境。比如重新啓動,ThreadLocal, * 收集統計,或添加日誌記錄。此外,terminated方法能被重寫執行任何特殊處理(需要去 * 執行一次之後徹底的銷燬)

如果鉤子或回調方法拋出異常,內部工作線程可能失敗和突然終止。

 

拒絕任務 

New tasks submitted in method execute(Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated. In either case, the execute method invokes the RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) method of its RejectedExecutionHandler. Four predefined handler policies are provided:

  1. In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection.
  2. In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted.
  3. In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped.
  4. In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.)

It is possible to define and use other kinds of RejectedExecutionHandler classes. Doing so requires some care especially when policies are designed to work only under particular capacity or queuing policies.

新任務通過execute方法提交將會被拒絕當執行器已經關閉,或者執行器使用有界且執行器的線程已經最大且隊列
已滿,飽和的。其中的任何一種情況,execute方法調用RejectedExecutionHandler 的rejectedExecution方
法。有四種預先定義的處理策略提供:
默認的處理策略是ThreadPoolExecutor.AbortPolicy。這個處理類會拋出一個運行時異常在拒絕的時候 

ThreadPoolExecutor.CallerRunsPolicy這個處理類會讓提交任務的線程自己去執行任務
 提供了一個簡單的反饋控制機制,這個機制將會減緩新任務的提交速度

ThreadPoolExecutor.DiscardPolicy這個處理類什麼都沒有做。也就是吧任務拋棄了。 

ThreadPoolExecutor.DiscardOldestPolicy這個處理類,如果執行器沒有關閉。
在隊列首部的任務將會被拋棄,染回執行器再重新提交任務(這可能再次的失敗,導致重複這個步驟)

任務的入隊

Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing:

  • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
任何阻塞隊列可以被用來傳輸和持有提交的任務,隊列的使用和線程池大小相互影響。
   如果少於核心線程數的線程在運行,Executor總是優先創建一個新的線程而不是進行入隊。
   如果有大於等於corePoolSize的線程正在運行,Executor總是優先使用入隊 而不是添加一個新的線程。
   如果一個請求不能被入隊,一個新的線程會被創建除非這個創建後會超過maximumPoolSize如果會超過則這個任務將會被拒絕。
There are three general strategies for queuing:
  1. Direct handoffs. A good default choice for a work queue is a SynchronousQueue that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed.
  2. Unbounded queues. Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server. While this style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed.
  3. Bounded queues. A bounded queue (for example, an ArrayBlockingQueue) helps prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter unacceptable scheduling overhead, which also decreases throughput.
這裏有三種普遍的策略爲隊列
直接處理:SynchronousQueue是一個好的默認選擇爲一個工作隊列,它直接將任務傳遞給線程而不用持有他們。
如果沒有線程立刻能夠去執行提交的任務,它將會失敗。這個策略會避免鎖定當請求的處理集有內部依賴關係。
Direct handoffs通常需要一個沒有邊界的maximumPoolSize去避免新的任務會被拒絕。這反過來有允許了當命
令到達的平均速度超過線程處理任務的速度時,無線線程增長的可能性
無邊界隊列:使用一個無邊界隊列比如一個沒有預先設定容量的LinkedBlockingQueue 將會導致新的任務在一個
隊列等待當素有的corePoolSize線程忙碌的時候。因此,沒有超過 corePoolSize大小的線程會被創建(並且
maximumPoolSize的值的設置是沒有效果的) 這可能是合適的當每個任務都是完全獨立的,因此任務之間的執行
不會相互影響。比如一個網頁服務。這個形式的隊列能用於平滑的處理短暫爆發的請求,它允許當指令到達的平均
速度比他處理的速度快時, 無邊界工作隊列增長的可能性。
有界隊列。一個有界隊列(比如ArrayBlockingQueue)幫助防止資源耗盡當使用 有限的maximumPoolSizes.但
是可能更難做到調整和控制。隊列大小和最大線程池大小可能交換:使用大的隊列和小的線程池減小cpu的使用,
系統資源和上下文切換的消耗,但是 可以導致認爲的低吞吐量。如果任務頻繁的阻塞,一個系統可能去計劃更多
的線程比你原本允許的。使用小的隊列通常需要打的線程池,這會是cpu持續的繁忙,但是可能遭遇調度頻繁,也
會導致吞吐率降低。

線程存活時間 

If the pool currently has more than corePoolSize threads, excess threads will be terminated if they have been idle for more than the keepAliveTime (see getKeepAliveTime(TimeUnit)). This provides a means of reducing resource consumption when the pool is not being actively used. If the pool becomes more active later, new threads will be constructed. This parameter can also be changed dynamically using method setKeepAliveTime(long, TimeUnit). Using a value of Long.MAX_VALUE TimeUnit.NANOSECONDS effectively disables idle threads from ever terminating prior to shut down. By default, the keep-alive policy applies only when there are more than corePoolSize threads. But method allowCoreThreadTimeOut(boolean) can be used to apply this time-out policy to core threads as well, so long as the keepAliveTime value is non-zero.

如果線程池當前有超過核心線程數的線程,超過的線程如果空閒的時間超過了 keeplive的時間將會被終止。它提供了一個種方法減少資源消耗在線程池沒有 * 活躍的使用時。如果現成詞在稍後變的活躍,新的線程將會被創建。這個參數也可以動態的改變。使用一個大的數有效不能使空閒的線程從任何時候終結在線程池關閉前。 默認情況下keep-alive方針應用只有在超過核心線程大小的線程上,但是可以通過方法 * allowCoreThreadTimeOut設置將這個超時方針應用到核心線程上,只要keepAliveTime不是非零的。

 

如何創建一個新的線程

New threads are created using a ThreadFactory. If not otherwise specified, a Executors.defaultThreadFactory() is used, that creates threads to all be in the same ThreadGroup and with the same NORM_PRIORITY priority and non-daemon status. By supplying a different ThreadFactory, you can alter the thread's name, thread group, priority, daemon status, etc. If a ThreadFactory fails to create a thread when asked by returning null from newThread, the executor will continue, but might not be able to execute any tasks. Threads should possess the "modifyThread" RuntimePermission. If worker threads or other threads using the pool do not possess this permission, service may be degraded: configuration changes may not take effect in a timely manner, and a shutdown pool may remain in a state in which termination is possible but not completed.

新線程被創建使用ThreadFactory.如果麼有其他說明,Executors的defaultThreadFactory 將會被使用(也就是沒有指定線程工廠的話會使用Executors的默認線程工廠), 默認的線程工廠創建的線程都在一個相同的ThreadGroup並且有相同的優先級和非守護狀態通過提供一個不同的線程工廠,你能改變線程的名稱,線程的group,優先級,守護狀態,等等。 如果線程工廠創建線程失敗當請求一個新線程返回null時,executor將繼續但是不能去執行任何任務。線程應該擁有修改線程。如果工作線程或其它線程使用線程池沒有次有許可,服務可能會降級:配置更改可能不會及時影響,關閉線程池可能保留一個終止狀態但是沒有完成。

按需構建

By default, even core threads are initially created and started only when new tasks arrive, but this can be overridden dynamically using method prestartCoreThread() or prestartAllCoreThreads(). You probably want to prestart threads if you construct the pool with a non-empty queue.

默認的,及時核心線程是最初創建和只有新的任務到達時創建,但是這能被動態覆蓋通過prestartCoreThread方
法。你可能想去預先啓動線程如果你構造了一個隊列不爲空的線程池。

 參考資料:

jdk1.8

發佈了33 篇原創文章 · 獲贊 0 · 訪問量 6183
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章