活到老學到老,大牛總結JAVA線程池,純源碼分享指南

線程池的工作原理,以及拒絕策略,大家都很熟悉,下面主要講一下線程池shutdown的原理,以及一些不常用操作的原理。

shutdown

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}
複製代碼

啓動有序關閉,在該關閉中執行先前提交的任務,但不接受任何新任務。如果已關閉,則調用不會產生任何其他影響。此方法不等待先前提交的任務完成執行。使用awaitTermination可以做到這一點。

advanceRunState

private void advanceRunState(int targetState) {
    for (;;) {
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}
複製代碼

將runState轉換爲給定狀態,或者已經存在的狀態比給定狀態大時將直接返回。 循環使用CAS設置狀態,設置成功返回。

interruptIdleWorkers

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();
    }
}
複製代碼

因爲work每次執行任務的時候都會先lock,完成任務後unlock, 如果tryLock可以成功說明work當前沒有在執行任務。使用interrupt中斷空閒的work線程。

tryTerminate

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            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
    }
}
複製代碼

shutdownNow

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;
}
複製代碼

嘗試停止所有正在執行的任務,暫停正在等待的任務的處理,並返回正在等待執行的任務的列表。從此方法返回後,這些任務將從任務隊列中耗盡(刪除)。此方法不等待主動執行的任務終止。除了盡最大努力嘗試停止處理正在執行的任務之外,沒有任何保證。此實現通過中斷取消任務,因此任何無法響應中斷的任務都可能永遠不會終止。

interruptWorkers

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}
複製代碼

中斷所有線程,即使處於活動狀態也是如此。

drainQueue

private List<Runnable> drainQueue() {
    BlockingQueue<Runnable> q = workQueue;
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    q.drainTo(taskList);
    if (!q.isEmpty()) {
        for (Runnable r : q.toArray(new Runnable[0])) {
            if (q.remove(r))
                taskList.add(r);
        }
    }
    return taskList;
}
複製代碼

使用drainTo方法將任務隊列寫入新列表。但是,如果隊列是DelayQueue或其他類型的隊列,但poll或drainTo可能無法刪除某些元素,則將它們逐個刪除。

Worker

Worker主要維護運行任務線程的中斷控制狀態,以及其他次要簿記。此類擴展了AbstractQueuedSynchronizer,以簡化獲取和釋放圍繞每個任務執行的鎖。我們實現了一個簡單的非可重入互斥鎖,而不是使用ReentrantLock,因爲我們不希望輔助任務在調用諸如setCorePoolSize之類的池控制方法時能夠重新獲取該鎖。另外,爲了抑制直到線程真正開始運行任務之前的中斷,我們將鎖定狀態初始化爲負值,並在啓動時將其清除(在runWorker中)。

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 {
        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);
    }
}
複製代碼

Work反覆從隊列中獲取任務並執行它們,同時解決許多問題:

  1. 我們可能從一個初始任務開始,在這種情況下,我們不需要第一個。否則,只要池是 運行時,我們從getTask獲得任務。如果返回null,則 Work由於池狀態或配置參數更改而退出。其他退出是由於引發異常 外部代碼,在這種情況下,completedAbruptly爲true,其通常導致processWorkerExit替換此線程。
  2. 在運行任何任務之前,先獲取鎖以防止在執行任務時其他池中斷,然後我們確保除非池正在停止,否則此線程沒有它的interrupt set。
  3. 在每次運行任務之前,都要調用beforeExecute,可能會引發異常,在這種情況下,我們導致線程死亡(中斷循環,使用completelyAbruptly爲true)無需處理 任務。
  4. 假設beforeExecute正常完成,我們運行 任務,收集其拋出的任何異常以發送給afterExecute。 我們分別處理RuntimeException,Error(兩者 規範保證我們可以捕獲)和任意Throwables。 因爲我們無法在Runnable.run中拋出Throwables,所以我們 將它們包裝在錯誤的出路(到線程的 UncaughtExceptionHandler)。 任何拋出的異常也保守地導致線程死亡。
  5. task.run完成後,我們調用afterExecute,這可能也會引發異常,這也會導致線程 死。 根據JLS Sec 14.20,此例外是即使task.run拋出也將有效。
  6. 異常機制的net效果是afterExecute和線程的UncaughtExceptionHandler具有相同的精度 我們可以提供的有關以下方面遇到的任何問題的信息用戶代碼。

processWorkerExit

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    tryTerminate();  // shutdown狀態時,每個工作線程完成工作後,終止線程池

    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);  // 工作線程執行任務異常退出時,重新啓動一個工作線程來完成任務
    }
}
複製代碼

線程池的狀態

線程池的控制狀態ctl是一個atomic integer。表示workCount和runState兩個字段。

workerCount指示有效線程數。int的後29位表示有效線程數。

runState,指示是否正在運行,正在關閉等。int的前三位表示線程池的狀態。

  1. RUNNING: 接受新任務並處理排隊的任務
  2. SHUTDOWN: 不接受新任務,而是處理排隊的任務
  3. STOP: 不接受新任務,不處理排隊任務以及中斷進行中的任務
  4. TIDYING: 所有任務已終止,workerCount爲零,線程轉換爲狀態TIDYING將運行Terminated()掛鉤方法
  5. TERMINATED: terminated()執行完成

運行狀態切換

  • RUNNING -> SHUTDOWN: 在調用shutdown()時,可能隱式在finalize()中
  • (RUNNING or SHUTDOWN) -> STOP: 調用shutdownNow()
  • SHUTDOWN -> TIDYING: 隊列和work pool爲空
  • STOP -> TIDYING: work pool爲空
  • TIDYING -> TERMINATED: terminated()執行完成

狀態相關的一些代碼

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

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

private static boolean runStateLessThan(int c, int s) {
    return c < s;
}

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

private static final int COUNT_BITS = Integer.SIZE - 3;  // 29,11101
private static final int CAPACITY   = (1 << COUNT_BITS) - 1; // 29位,全是1,值 536870911

private static final int RUNNING    = -1 << COUNT_BITS;  // 32位,前三位是1, 值 -536870912
private static final int SHUTDOWN   =  0 << COUNT_BITS;  // 值 0
private static final int STOP       =  1 << COUNT_BITS;  // 30位,第一位是1,值 536870912
private static final int TIDYING    =  2 << COUNT_BITS;  // 31位,第一位是1,值 1073741824
private static final int TERMINATED =  3 << COUNT_BITS;  // 31位,前兩位是1, 值 1610612736

-1  // 11111111111111111111111111111111, 32位
0   // 0
1   // 1
2   // 10
3   // 11

~CAPACITY // 32位,前三位是1,11100000000000000000000000000000,值-536870912 

 

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