JDK執行緒池原始碼研究

minororange發表於2021-11-12

主要構成

workers: 工作組
queue: 任務佇列
threadFactory: 執行緒生產工廠
handler: 異常處理

執行緒池狀態

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;

1. RUNNING:執行緒池一旦被建立,就處於 RUNNING 狀態,任務數為 0,能夠接收新任務,對已排隊的任務進行處理。

2. SHUTDOWN:不接收新任務,但能處理已排隊的任務。呼叫執行緒池的 shutdown() 方法,執行緒池由 RUNNING 轉變為 SHUTDOWN 狀態。

3. STOP:不接收新任務,不處理已排隊的任務,並且會中斷正在處理的任務。呼叫執行緒池的 shutdownNow() 方法,執行緒池由(RUNNING 或 SHUTDOWN ) 轉變為 STOP 狀態。

4. TIDYING:

  • SHUTDOWN 狀態下,任務數為 0, 其他所有任務已終止,執行緒池會變為 TIDYING 狀態,會執行 terminated() 方法。執行緒池中的 terminated() 方法是空實現,可以重寫該方法進行相應的處理。
  • 執行緒池在 SHUTDOWN 狀態,任務佇列為空且執行中任務為空,執行緒池就會由 SHUTDOWN 轉變為 TIDYING 狀態。
  • 執行緒池在 STOP 狀態,執行緒池中執行中任務為空時,就會由 STOP 轉變為 TIDYING 狀態。

5. TERMINATED:執行緒池徹底終止。執行緒池在 TIDYING 狀態執行完 terminated() 方法就會由 TIDYING 轉變為 TERMINATED 狀態。

參考連結

主要流程

建立執行緒池 -> 建立 worker 或放入任務佇列 -> 執行緒開始執行任務
原始碼流程:

execute

JDK執行緒池原始碼研究

        // 獲取執行緒池控制程式碼
        int c = ctl.get();
        // 當前執行緒數量是否小於設定的核心執行緒池大小
        if (workerCountOf(c) < corePoolSize) {
            // 新增一個 woker
            if (addWorker(command, true))
               // 新增成功後推出
                return;
            // 重新整理執行緒池控制程式碼
            c = ctl.get();
        }
        // 以下是執行緒池數量達到上限後的處理
        // 如果當前執行緒池是 RUNNING,且新增任務佇列成功
        if (isRunning(c) && workQueue.offer(command)) {
            // 重新整理執行緒池控制程式碼
            int recheck = ctl.get();
            // 如果當前執行緒池非 RUNNING狀態,則移除任務
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果是RUNNING狀態或移除任務失敗,且當前工作執行緒為0
            else if (workerCountOf(recheck) == 0)
                // 新增一個沒有任務的 worker 去執行任務佇列中剩餘的任務
                addWorker(null, false);
        }
        // 如果執行緒池非RUNNING或,新增任務佇列失敗,則新增一個 woker 執行任務
        // 如果增加 worker 失敗則丟擲異常拒絕任務
        else if (!addWorker(command, false))
            reject(command);

addWorker

JDK執行緒池原始碼研究

  • 判斷執行緒狀態,增加 woker 數量

           retry:
          for (;;) {
              // 獲取執行緒池控制程式碼
              int c = ctl.get();
              // 當前執行緒池狀態
              int rs = runStateOf(c);
    
              // 1.如果當前執行緒池狀態為 STOP 及以上,返回false
              // 2.如果當前執行緒狀態為 SHUTDOWN,且任務和任務佇列都為空,返回false
              if (rs >= SHUTDOWN &&
                  ! (rs == SHUTDOWN &&
                     firstTask == null &&
                     ! workQueue.isEmpty()))
                  return false;
    
              for (;;) {
                  // 獲取執行緒數量
                  int wc = workerCountOf(c);
                  // 如果執行緒數量大於系統最大值,或大於設定的最大執行緒數量,返回false
                  if (wc >= CAPACITY ||
                      wc >= (core ? corePoolSize : maximumPoolSize))
                      return false;
                  // CAS 操作 worker 數量,操作成功後跳出最外層迴圈,開始執行任務
                  if (compareAndIncrementWorkerCount(c))
                      break retry;
                  // 重新整理執行緒池控制程式碼
                  c = ctl.get();  // Re-read ctl
                  // 如果當前執行緒池狀態有變化,跳出第二層迴圈,
                  // 即重新 check 執行緒池、任務、任務佇列狀態
                  if (runStateOf(c) != rs)
                      continue retry;
                  // else CAS failed due to workerCount change; retry inner loop
              }
          }
  • 建立 woker,執行緒執行任務

          // woker 是否啟動標記
          boolean workerStarted = false;
          // woker 是否新增進 woker 組標記
          boolean workerAdded = false;
          Worker w = null;
          try {
              // 建立 woker,在 woker 中透過執行緒工廠會建立執行緒
              w = new Worker(firstTask);
              // 獲取 woker 的執行緒
              final Thread t = w.thread;
              if (t != null) {
                  // 獲取全域性鎖
                  final ReentrantLock mainLock = this.mainLock;
                  // 阻塞取鎖
                  mainLock.lock();
                  try {
                      // 重新獲取執行緒池狀態
                      int rs = runStateOf(ctl.get());
                      // 如果執行緒池是 RUNNING 狀態
                      // 或者是 (SHUTDOWN 狀態 且任務為 null)-> excute 中刪除失敗的任務
                      if (rs < SHUTDOWN ||
                          (rs == SHUTDOWN && firstTask == null)) {
                          // 如果當前執行緒已經啟動過了,丟擲異常
                          if (t.isAlive()) // precheck that t is startable
                              throw new IllegalThreadStateException();
                          // 講 woker 新增進 woker 組中
                          workers.add(w);
                          // 記錄執行緒峰值
                          int s = workers.size();
                          if (s > largestPoolSize)
                              largestPoolSize = s;
                          // woker新增成功標記
                          workerAdded = true;
                      }
                  } finally {
                      // 釋放全域性鎖
                      mainLock.unlock();
                  }
                  // 如果新增 woker 時沒有異常,開始執行任務,標記 woker 已開始工作
                  if (workerAdded) {
                      t.start();
                      workerStarted = true;
                  }
              }
          } finally {
              // 如果 woker 啟動失敗,執行新增失敗的策略 
              if (! workerStarted)
                  // woker 組中移除 woker
                  // 減少 woker 組 woker 總數
                  // 嘗試結束執行緒池
                  addWorkerFailed(w);
          }
          // 返回 woker 是否開始
          return workerStarted;

runWorker

Woker 本身是一個 Runnable 物件,在新建 Thread 物件時,執行緒工廠會把 Woker 傳給 Thread,Thread.start 時,會呼叫 Woker.run,Woker.run 再呼叫外部的 runWoker 方法

JDK執行緒池原始碼研究

        // 獲取當前執行緒
        Thread wt = Thread.currentThread();
        // 獲取當前任務
        Runnable task = w.firstTask;
        // 將 woker 的任務設定為空
        w.firstTask = null;
        // 在建立 worker 時,會將狀態標記為 -1 ,執行任務前將狀態改為 0
        // -1 時不允許結束這個 woker
        // 防止 worker 剛加入 worker 組還沒開始執行任務就被回收了
        w.unlock(); // allow interrupts
       // 執行緒是否異常退出的標記
        boolean completedAbruptly = true;
        try {
            // 當任務不為空,或任務佇列的任務不為空時
            while (task != null || (task = getTask()) != null) {
                // 鎖住執行緒
                w.lock();
                //1. 執行緒池如果是 STOP 狀態,且當前執行緒不是中斷狀態,則設定中斷狀態
                //2. 執行緒池不是 STOP 狀態,重新整理當前執行緒執行緒的中斷狀態,重新整理後再次獲取執行緒池狀態,如果是 STOP 且當前執行緒不是中斷狀態,則設定中斷狀態
                // 第二次獲取執行緒池狀態是防止重新整理執行緒中斷狀態後執行緒池結束,所以重新整理後再 check 一次
                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;
                    // 增加 woker 的完成任務數量
                    w.completedTasks++;
                    // 釋放當前 woker 的鎖
                    w.unlock();
                    // 如果沒有異常丟擲則繼續 while 迴圈
                }
            }
            // 異常退出走不到這裡
            completedAbruptly = false;
        } finally {
            // 處理 while 迴圈結束後的工作
            // 如果是異常退出,則新增一個 woker 代替
            // 如果是正常退出,則清理 woker 組,嘗試關閉執行緒池
            processWorkerExit(w, completedAbruptly);
        }

getTask

JDK執行緒池原始碼研究

        // 是否獲取任務超時的標記
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            // 執行緒池控制程式碼
            int c = ctl.get();
            // 執行緒池狀態
            int rs = runStateOf(c);

            // 如果當前執行緒池是 STOP 及以上狀態,則減少 woker 數量,返回 null
            // 如果當前執行緒是 SHUTDOWN 且任務佇列為空,則減少 woker 數量,返回 null
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            // 當前執行緒數量
            int wc = workerCountOf(c);

            // 是否允許超時
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            // 1. 執行緒總數大於最大執行緒數且任務佇列為空,減少 woker 數量,減少成功後返回 null,減少失敗時回到迴圈第一行
            // 2. 執行緒總數小於或等於最大執行緒數,允許超時且已超過時,且任務佇列為空或執行緒數大於1,減少 woker 數量,減少成功後返回 null,減少失敗時回到迴圈第一行
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
               // 獲取任務,如果允許超時,則使用 poll 方法並設定超時時間
               // 否則使用 take 方法,
               // 在 take 方法中,如果任務佇列為空會呼叫 unsafe.park 將執行緒掛起
               // workQueue.offer 新增任務時,會呼叫 unsafe.unpark 喚醒一個執行緒
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                // 如果任務不會 null 則返回任務
                if (r != null)
                    return r;
                // 任務為 null 將超時標記為 true
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章