【Thread】- ThreadPoolExecutor執行過程分析

public static void main(String[] args) throws Exception{
        int corePoolSize = 5;
        int maximumPoolSize = 10;
        int blockingQueueSize = 13;
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize, //最小線程數
                maximumPoolSize, //最大線程數
                100, TimeUnit.MILLISECONDS, //超過最小線程數的線程,空閒多久則回收
                new ArrayBlockingQueue<>(blockingQueueSize, true)//放到某個隊列裏
        );
        //是否去掉非活動線程
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        //默認創建最小線程數的線程
        threadPoolExecutor.prestartAllCoreThreads();
        Set<Long> threadId = new HashSet<>();
        long threadsStartTime = System.currentTimeMillis();
        for (int i = 0; i < 20; i++) {
            threadPoolExecutor.execute(() -> {
                try {
                    long threadStartTime = System.currentTimeMillis();
                    Thread thread = Thread.currentThread();
                    long id = thread.getId();
                    long subTime = threadStartTime - threadsStartTime;
                    System.out.println("threadsStartTime:"+threadsStartTime+",threadStartTime:"+threadStartTime+",subTime:"+ subTime +",id:"+id);
                    Thread.sleep(3000);
                    if(threadId.contains(id)){
                        System.out.println("已存在的線程執行任務:"+id +":"+thread.getName());
                    }else{
                        threadId.add(id);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        System.in.read();
    }

測試

  • 創建20個任務,阻塞隊列長度爲18,線程池最小線程數爲5,最大線程數爲10

在這裏插入圖片描述
從上圖可知,線程池創建了5個線程處理了5個任務,15個任務扔進了隊列

  • 創建20個任務,阻塞隊列長度爲13,線程池最小線程數爲5,最大線程數爲10
    在這裏插入圖片描述
    從上圖可知,線程池創建了7個線程處理了7個任務,13個任務扔進了隊列
  • 創建20個任務,阻塞隊列長度爲9,線程池最小線程數爲5,最大線程數爲10
    在這裏插入圖片描述
    從上圖可知,線程池創建了10個線程處理了10個任務,9個任務扔進了隊列,一個任務被拒絕

源碼分析

基於測試結果,應該對其執行過程有了初步的瞭解

execute(Runnable command)

/**
 * Executes the given task sometime in the future.  The task
 * may execute in a new thread or in an existing pooled thread.
 *
 * If the task cannot be submitted for execution, either because this
 * executor has been shutdown or because its capacity has been reached,
 * the task is handled by the current {@code RejectedExecutionHandler}.
 *
 * @param command the task to execute
 * @throws RejectedExecutionException at discretion of
 *         {@code RejectedExecutionHandler}, if the task
 *         cannot be accepted for execution
 * @throws NullPointerException if {@code command} is null
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        //如果線程池中線程數沒有達到corePoolSize,則新增線程(worker)
        if (addWorker(command, true))
            return;
        //更新c值。
        c = ctl.get();
    }
    //線程池處於RUNNING狀態,並且阻塞隊列未滿
    //workQueue.offer(command)是非阻塞方法,當隊列滿時直接返回false(例如,SynchronousQueue如果沒有線程在阻塞take,則返回false)
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //再次檢查狀態,如果發現不是RUNNING狀態,則remove掉剛纔offer的任務。
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //如果有效線程數==0,添加一個線程,而不去啓動它。??
        //怎麼會==0?
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //如果不是RUNNING狀態,或者阻塞隊列已滿,則添加線程
    //如果不能添加,則reject。
    //false 表示添加的線程屬於maximumPoolSize,如果線程數已經達到maximumPoolSize,則reject
    else if (!addWorker(command, false))
        reject(command);
}

阻塞隊列(BlockingQueue) 的一些操作方法

* 拋出異常 特殊值 阻塞 超時
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
檢查 element() peek() 不可用 不可用

addWorker(Runnable firstTask, boolean core)

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()))
            //1. 處於 STOP, TYDING 或 TERMINATD 狀態 並且 
            //2. 不是SUHTDOWN 或者 firsttask != null 或 queue不爲空
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            //wc大於最大容量。
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                //沒有空餘的線程了。
                return false;
            //有效線程數加一,加一成功後break
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            //runState改變,從頭執行邏輯。
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
            //else runState 沒變,重新去執行加一操作。
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        //創建worker
        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) {
                //添加成功,啓動線程
                //啓動後執行runWorker(this);
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

runWorker(Worker w)
運行worker,該線程不斷的getTask()從隊列中獲取任務,然後 task.run();運行。只要隊列中有值則不斷循環。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        //getTask()方法是個無限循環, 會從阻塞隊列 workQueue中不斷取出任務來執行.
        //addWorker(null, false);情況,task==null,這樣就需要getTask從隊列中取任務執行(自己不帶任務)。直到getTask返回null
        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()

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        // STOP以上狀態,或者SHUTDOWN狀態下queue爲空,即都沒有任務要執行了。
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            //線程數減一
            decrementWorkerCount();
            //該線程退出。
            return null;
        }
        //下面都是RUNNING狀態,或SHUTDOWN狀態queue!=null

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //設置了allowCoreThreadTimeOut,或者線程數大於core線程數。
        //是否剔除超時的線程?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        // 通過返回 null 結束線程。
        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();
               //線程已經準備好,正在take(),沒有什麼標誌位?
            
            //取出runnable 返回
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

結論

線程池執行任務流程

  • 當線程數小於線程池的最小線程數時,會創建線程去執行任務
  • 當線程池的線程數大於等於線程池的最小線程數時,如果線程池處於運行狀態,且阻塞隊列沒滿時,則將任務塞進隊列
  • 如果隊列滿時,但線程池的線程數小於最大線程數時,直接創建線程去處理任務
  • 否則拒絕任務
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章