java源碼學習-線程池ThreadPoolExecutor

main方法

先貼上我做實驗使用的main方法

public class ThreadPoolExecutorTest {

    public static void main(String[] args) {
        int corePoolSize = 2;
        int maximumPoolSize = 4;
        BlockingQueue<Runnable> blockingDeque = new ArrayBlockingQueue<>(2);
        RejectedExecutionHandler handle = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0,
                TimeUnit.SECONDS, blockingDeque, handle);

        Thread[] threads = new Thread[6];
        for(int i = 0; i < 6; i ++) {
            int finalI = i;
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("start: " + finalI);
                        Thread.sleep(1000);
                        System.out.println("end: " + finalI);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        for(Thread thread: threads) {
            threadPoolExecutor.submit(thread);
        }
        System.out.println(threadPoolExecutor.getActiveCount());
        threadPoolExecutor.shutdown();
    }
}

構造器

這裏只貼一個, 其他構造方法都是直接使用這個的

參數名稱 作用
corePoolSize 初始線程池的線程個數
maximumPoolSize 線程池中最大線程個數
keepAliveTime 線程存活時間
unit 存活時間的單位
workQueue 一個阻塞隊列
threadFactory 線程工廠
handler 採取的飽和政策(拒絕的策略)
拒絕策略 行爲
AbortPolicy 拒絕時會拋出RejectedExecutionException異常
DiscardPolicy 直接忽略
DiscardOldestPolicy 丟棄執行隊列中最老的任務, 嘗試提供位置給當前任務
CallerRunsPolicy 交給任務提交者執行

拒絕策略默認爲AbortPolicy, 構造器中前五個參數都是必填參數, 線程工廠默認爲Executors中的defaultThreadFactory

    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;
    }

Worker內部類

Worker繼承了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;

        /** 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;

        // 構造器, 從工廠中獲取新線程
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        // 這個任務的run方法調用在ThreadPoolExecutor中的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) {
                }
            }
        }
    }

executor

executor是將任務提交到線程池, 這是使用線程池的第二步, 第一步當然是new一個線程池
這個方法裏面有三個判斷

  1. 當前正在執行的任務小於corePoolSize, 也就是線程池還有空餘, 那麼直接執行任務, 否則進入2
  2. 將當前任務放入阻塞隊列, 等待直至線程池中有空閒線程
  3. 如果阻塞隊列滿了, 就會判斷之前設置的maximumPoolSize最大線程個數, 如果當前執行的任務個數小於maximumPoolSize, 那麼就會創建新線程去執行這個任務
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        // ctl初始值是一個很大的負數, 這個負數同CAPACITY進行與運算可以得出正在運行的任務個數
        int c = ctl.get();
        // 這是第一個判斷
        if (workerCountOf(c) < corePoolSize) {
        	// 在addWorker中通過true和false來判斷這是第一步還是第三步
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // isRunning方法用於判斷線程池是否還在工作
        // 如果還在工作就將任務加入阻塞隊列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 重新進行工作檢測, 在併發情況下可能會出現工作狀況被別的線程修改的情況
            // 如果線程池結束工作了並且阻塞隊列中移除當前任務成功, 則進行飽和策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 否則判斷當前正在執行的任務個數是不是0
            // 如果是則添加一個空任務
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 阻塞隊列滿了, 第三步
        else if (!addWorker(command, false))
        	// 添加任務失敗則進行飽和策略
            reject(command);
    }

addWorker

這個方法是線程池中很重要的方法, 用於執行任務

private boolean addWorker(Runnable firstTask, boolean core) {
		// break標記
        retry:
        for (;;) {
            int c = ctl.get();
            // 獲取當前線程池狀態
            int rs = runStateOf(c);

            // 如果rs是終止狀態或者其他三種狀態(這些狀態都不接受新任務)並且
            // rs不是終止狀態或者傳入的任務爲空, 或者阻塞隊列爲空
            // 就會進入語句塊, 返回false
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                // wc是線程正在執行的任務個數
                // 如果wc大於線程池所能承受的最大數量CAPACITY = 536,870,911(二進制中29個1)
                // 否則, 看是executor的第幾步來判斷是否大於corePoolSize 或者maximumPoolSize
                // 如果上述兩個條件滿足一個則返回false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 使用cas操作將正在執行的任務+1, 成功則跳出最外側循環
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                // cas失敗了, 則更新c的數值
                c = ctl.get();  // Re-read ctl
                // 判斷rs狀態是否改變
                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 {
                    // 獲取rs的狀態
                    int rs = runStateOf(ctl.get());
					// 這裏不需要進行跟上面一樣的重複判斷, 因爲這裏是同步的
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        // 判斷t線程是否已經存在, 因爲這裏需要啓動線程
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
                        // 將w加入到workers中, workers是一個被final修飾的hashSet
                        // 這裏提一句被final修飾的類或者對象我們是可以改變其內部值的
                        // final修飾對象時僅僅是不可改變其引用地址
                        // 因爲workers的所有方法在使用前都會加上鎖, 所以這裏可以直接獲取size
                        workers.add(w);
                        int s = workers.size();
                        // largestPoolSize初始是0, 用於記錄最大的同時執行任務數
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 任務添加成功就執行線程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
        	// 任務執行失敗就調用添加任務失敗方法來移除任務
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

runWorker

在runWorker中已經創建的Worker會一直存在, 並且會自旋, 獲取阻塞隊列中的任務執行就是在這個方法中, 個人理解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 {
        	// 這裏的循環如果初始傳入的Worker中無任務, 則使用getTask方法從阻塞隊列中獲取
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 當前線程池是否大於STOP狀態, 或者當前線程需要中斷並清除中斷狀態, 並且
                // 這時的線程池狀態是大於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, 該Worker的完成任務數量+1
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
        	// task爲空並且阻塞隊列中無任務則關閉該Worker
            processWorkerExit(w, completedAbruptly);
        }
    }

不難發現, 只要阻塞隊列中還有任務, 已經存在的Worker就不會關閉, 而是去執行阻塞隊列中的任務

private void processWorkerExit(Worker w, boolean completedAbruptly) {
		// 這個標誌在上文中, 如果是正常的try流程則會爲false
		// 如果是異常了就會是true, 這裏就會進入decrementWorkerCount方法
		// 使用cas操作將ctl- 1
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	// 更新總完成數, 在hashSet中移除該Worker
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
		// 判斷線程池狀態嘗試終止線程池
        tryTerminate();

        int c = ctl.get();
        // 如果當前線程池狀態是不需要被關閉的, 即小於STOP的
        if (runStateLessThan(c, STOP)) {
        	// 判斷是否正常中止, 如果是則說明所有任務都已完成就進入該語句塊
            if (!completedAbruptly) {
            	// min默認是corePoolSize, 如果允許超時則是0
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                // 如果min是0阻塞隊列是不爲空的, 那麼必須要保證有一個工作線程
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                // 如果當前線程數量大於等於最小工作線程數量則直接返回
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            // 因爲不是正常中止, 所以該Worker不該被中止, 添加一個空的Worker
            // 這樣可以繼續執行阻塞隊列中的任務
            addWorker(null, false);
        }
    }

getTask

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
		// 這是一個死循環, 即必須返回一個元素
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 判斷線程池狀況, 如果下面情況同時滿足則將ctl - 1
            // 1.狀態>=SHUTDOWN, 即需要被中止
            // 2.狀態>=STOP, 即需要被中止並且不完成已添加的任務, 或者阻塞隊列爲空
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
			// 獲取工作線程個數
            int wc = workerCountOf(c);

            // 判斷工作線程個數是否大於核心線程個數, 這裏的情況是阻塞隊列曾經滿過, 直接開新線程
            // allowCoreThreadTimeOut是默認爲false的, 我們假設他這裏也爲false
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			
			// 如果工作線程數量大於最大線程數量, 或者
			// timed是true並且timedOut是true(這裏timedOut默認爲false)
			// 並且工作線程個數大於1或者阻塞隊列爲空
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 滿足條件就將wc-1, 因爲wc大於最大線程數量或者已經超時
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            	// timed爲true則將阻塞隊列的隊列頭出列, 這裏就是阻塞隊列中的任務獲得過程
            	// 返回到runWorker的while中執行
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                	// 返回r, 可以給Worker執行
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

後記

在學習ThreadPoolExecutor源碼的過程中遇到最大的問題就是, 一直在想阻塞隊列中的任務是如何執行的, 線程池爲什麼可以節省創建線程的時間, 線程池中是否創建的時候就自帶線程, 這些問題在學習完源碼之後就都解決了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章