深入理解Java線程池ThreadPoolExecutor

1:先看下線程池屬性配置

public ThreadPoolExecutor(int corePoolSize,    //核心線程池個數
                          int maximumPoolSize, //最大線程池個數
                          long keepAliveTime, //非核心線程存活時間
                          TimeUnit unit,      //時間格式
                          BlockingQueue<Runnable> workQueue,  //存放Runnable的工作隊列
                          ThreadFactory threadFactory,      //創建線程所需工廠
                          RejectedExecutionHandler handler //超出workQueue最大等待隊列與maximumPoolSize之和會回調此接口) 
  

1:corePoolSize(核心線程池個數)核心線程會一直存在,並且一直運行,知道關閉線程池或者線程被中斷。

2:maximumPoolSize(最大線程池個數)就是該線程池允許創建線程的最大個數。

3:unit(時間格式)時,分,秒等。

4:workQueue(存放Runnable的工作隊列)

(1)當corePoolSize爲5,workQueue的長度爲5,則同時創建5個核心線程取執行這5個工作隊列。也就是說workQueue的長度小於等於corePoolSize時會創建workQueue長度的線程取執行工作隊列(我使用JDK1.8是這樣的,1.6的不確定)。

(2)當工作隊列workQueue的size大於核心線程池數並且小於阻塞隊列的個數(用ArrayBlockingQueue可以設置等待隊列的個數),則只會創建全部的核心線程取執行工作隊列裏的數據,其他隊列處於阻塞狀態。

(3)當工作隊列workQueue的size大於核心線程池數並且大於等待隊列的個數,則會新建線程執行隊列。新建線程個數=工作隊列個數 - 核心線程池數 - 阻塞隊列數。

(3)當工作隊列workQueue的size > (maximumPoolSize+工作隊阻塞待個數)  會回調 handler 將超出的隊列拋出。需要在回調處將隊列重新加到線程池裏,否則該隊裏的功能將不再執行。此時回調與執行execute在同一個線程,若是主線程,隊列一直處於超出狀態,主線程會卡死。

2:內部執行原理

(1)調用執行方法

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))  // 當工作隊列workQueue的size > (maximumPoolSize+工作隊列阻塞個數)
            reject(command);
    }

(2)addWorker方法

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
/**
走出該死循環並繼續執行該方法之後代碼條件是:小於核心線程數或者大於核心線程數與阻塞數之和,或者核心線程未開啓
*/
        for (;;) {
           ……  //代碼省略
        }
……
        try {
            w = new Worker(firstTask);      //創建線程,將該工作任務綁定到該線程
            final Thread t = w.thread;
            if (t != null) {
              ……
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);                                                  //將創建的線程存入線程隊列裏
                      ……                  

  }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();                             //開啓線程
                    workerStarted = true;
                }
            }
        } …….
        return workerStarted;
    }

 

(3)Worker 類

Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);       //創建線程
}

(4)當線程啓動時會執行Worker 類中的run方法

public void run() {
    runWorker(this); //繼續跟蹤該方法
}

(5)runWorker方法

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()方法是有關核心線程如何回收重用的,
            while (task != null || (task = getTask()) != null) {   
             ……
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

(6)getTask()方法

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
//該循環是死循環,當爲核心線程時,隊列爲空,則一直循環,不爲空則返回給runWorker(Worker w) 中的while循環,當非核心線程,則存活到keepAliveTime時間,會跳出該循環,同時也滿足退出runWorker(Worker w) 中的while循環,所以該線程結束生命週期。
        for (;;) {
       ……
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :        //當爲非核心線程,執行該代碼
                    workQueue.take();     //當爲核心線程執行該行。一直從隊列拿數據,所以外面只要往線程池加隊列,就會立馬執行。所以核心線程一直死循環存在。解釋了核心線程爲什麼能夠被重新利用                                           
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

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