Java執行緒池

流浪者&喬木發表於2020-09-23

1. 簡介

在引入執行緒池之前,我們先來了解幾個事情:

  • 執行緒的建立和銷燬是有代價的,如執行緒建立需要時間和相關計算資源。如果在Web伺服器上為每個來到的請求都建立一個執行緒,而大多數請求都是輕量級的處理過程。那麼建立執行緒的代價與請求處理的代價相比就非常大了,導致影響整體效能。
  • 當執行緒數量達到能讓CPU忙綠起來的時候,此時再建立執行緒,執行緒也基本處於閒置狀態,這時候多出來的執行緒除了佔用記憶體外,還可能因為與其他執行緒爭用CPU資源導致出現其他效能開銷.
  • 在可建立執行緒的數量上存在一個限制,如果超過這個限制,可能會丟擲OutOfMemoryError異常。

這時候如果能出現一個東西能夠對執行緒的生命週期進行管理,對現有的執行緒重複利用,並且能夠以一種簡單的方式將任務的提交與執行相解耦。沒錯,這就是執行緒池(Thread Pool),在要了解Java中的執行緒池,首先必須瞭解ThreadPoolExecutor這個類。

2. ThreadPoolExecutor詳解

類繼承圖

ThreadPoolExecutor類繼承

建構函式

/執行緒池配置資訊,volatile修飾保證變數在多執行緒下的可見性
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private volatile long keepAliveTime;
private final BlockingQueue<Runnable> workQueue;
private volatile ThreadFactory threadFactory;
private volatile RejectedExecutionHandler handler;

private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();

private static final RuntimePermission shutdownPerm =
    new RuntimePermission("modifyThread");

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
複製程式碼

從上面的JDK中ThreadPoolExecutor類的建構函式原始碼看出該建構函式一共有7個引數,下面介紹七個引數的含義:

引數 含義
corePoolSize 基本大小,即執行緒池中的核心執行緒數
maximumPoolSize 最大大小,即執行緒池中允許的最大執行緒數
keepAliveTime 存活時間,當執行緒的沒執行任務時,空閒的時間超過了這個時間就會被標記為可回收,直到執行緒池的大小超過基本大小,被標記的執行緒就會被終止
unit keepAliveTime的單位,有DAYSHOURSMINUTESSECONDSMILLISECONDSMICROSECONDSNANOSECONDS7個單位可選
workQueue 工作佇列,一個用來儲存等待被執行的任務的阻塞佇列
threadFactory 執行緒工廠。執行緒池在建立執行緒時通過呼叫執行緒工廠的Thread newThread(Runnable r)來建立執行緒
handler 飽和策略。當阻塞佇列已滿、執行緒池當前的執行緒數已達到最大值且沒有執行緒處於空閒狀態時,此時對於提交過來的任務將執行飽和策略。(如果某個任務提交到一個已關閉的Executor時,也會執行飽和策略)

ThreadPoolExecutor類中有四個過載的建構函式,每個建構函式都必須指定上表中的前5個引數,最後兩個引數可以隨意指定,不指定的話建構函式會使用預設的執行緒工廠飽和策略

執行緒工廠(ThreadFactory)

執行緒池建立執行緒都是通過的ThreadFactoryThread newThread(Runnable r)方法來建立的。下面是Executors類裡的預設執行緒工廠方法的原始碼。

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }
複製程式碼

從上面可以看出預設執行緒工廠建立出的是一個非守護、優先順序為Thread.NORM_PRIORITY 的執行緒。如果想要自己定製執行緒工廠滿足需求,只需實現ThreadFactory介面的Thread newThread(Runnable r)方法。

飽和策略(RejectedExecutionHandler)

JDK中的ThreadPoolExecutor類提供了4種不同的RejectedExecutionHandler實現:

  • AbortPolicy 預設的飽和策略,該策略丟擲未檢查(執行時異常)的RejectedExecutionException
  • DiscardPolicy 不執行任何操作,直接拋棄任務
  • CallerRunsPolicy 在呼叫者執行緒中執行該任務
  • DiscardOldestPolicy 丟棄阻塞佇列中的第一個任務, 然後重新將該任務交給執行緒池執行

同樣的,可以通過實現RejectedExecutionHandler介面自定義飽和策略。

執行緒池狀態和執行緒數量

/代表執行緒池當前狀態和執行緒數量的原子變數
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;  /COUNT_BITS為29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1; /CAPACITY為能表示的最大執行緒數。

/執行緒池狀態
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;

/對執行緒池狀態和執行緒數量進行打包和拆包的函式:
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;
}

/執行緒數量增1,成功返回true,失敗返回false
private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }

/執行緒數量減1,成功返回true,失敗返回false
private boolean compareAndDecrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect - 1);
}

/執行緒數量減1,失敗則重試直到成功
private void decrementWorkerCount() {
    do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}

複製程式碼

AtomicInteger型別的變數ctl用高3位來表示當前執行緒池狀態,低29位來表示當前的執行緒數。

Java執行緒池有5種不同的狀態,分別為執行(RUNNING)、關閉(SHUTDOWN)、停止(STOP)、整理(TIDYING)、結束(TERMINATED)。 在ThreadPoolExecutor裡由5個整型常量表示,每個整型常量的都由高3位表示狀態:

  • RUNNING 高3位為111,該狀態的執行緒池會接收新任務,並處理阻塞佇列中的任務
  • SHUTDOWN 高3位為000,該狀態的執行緒池不會接收新任務,但會處理阻塞佇列中的任務。呼叫void shutdown()方法實現
  • STOP 高3位為001,該狀態的執行緒不會接收新任務,也不會處理阻塞佇列中的任務,而且會中斷正在執行的任務。呼叫List<Runnable> shutdownNow()實現。
  • TIDYING 高3位為010,當執行緒池關閉後阻塞佇列的任務已完成或執行緒池停止,然後workerCount(當前執行緒數量)為0,執行緒池進入該狀態後會呼叫terminated()方法進入TERMINATED狀態。
  • TERMINATED 高3位為011

啟動執行緒池

當建立完一個ThreadPoolExecutor物件後,執行緒池裡並沒有執行緒。一般都是呼叫void execute(Runnable command)執行任務時才建立執行緒並啟動,不過可以通過呼叫如下方法預先建立核心執行緒並啟動(在addWorker方法裡啟動):

public int prestartAllCoreThreads() {
        int n = 0;
        while (addWorker(null, true))
            ++n;
        return n;
    }
複製程式碼

執行過程

執行過程
如上圖所示,當呼叫void execute(Runnable command)這個方法執行任務時:

  1. 判斷當前執行緒池執行緒數量是否小於核心執行緒池大小,是則建立執行緒並啟動,否則到第2步
  2. 判斷任務佇列是否已滿,未滿則將任務加入阻塞佇列,已滿則到第3步
  3. 判斷當前執行緒池執行緒數量是否小於最大執行緒池大小,是則建立執行緒並啟動,否則執行飽和策略
public void execute(Runnable command) {
    /任務為空,丟擲空指標異常
    if (command == null)
        throw new NullPointerException();
   
    int c = ctl.get();
   
   /判斷當前執行緒數量是否小於核心執行緒數
    if (workerCountOf(c) < corePoolSize) {  
        /是則新增一個核心執行緒(true表示核心執行緒)到執行緒池,並且啟動執行緒執行任務(addWorker方法裡會啟動)
        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);
        /否則若處於執行狀態或移除失敗,這時無論處於哪種情況任務都在阻塞佇列裡,判斷當前執行緒數量是否為0
        else if (workerCountOf(recheck) == 0)
            若是則新增一個執行緒並啟動
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}   
複製程式碼

addWorker方法

boolean addWorker(Runnable firstTask, boolean core)方法的作用就是建立Worker物件並啟動這個物件裡的執行緒(Worker裡一個Thread型別的欄位)。

private final ReentrantLock mainLock = new ReentrantLock();
private final HashSet<Worker> workers = new HashSet<Worker>();
private int largestPoolSize;

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        
        /如果執行緒池不處於執行狀態,理論上不應該新增一個執行該任務的執行緒,但如果滿足下面三個條件的話就可以通過:
        1. 執行緒池狀態是關閉
        2. 要執行的任務為空
        3. 阻塞佇列不為空
        因為執行緒池關閉後不允許提交任務,但關閉後會執行完阻塞佇列的任務,所以允許新增一個firstTask為空的執行緒
        來幫助執行完阻塞佇列裡的任務
        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;
            /到這裡前面的限制條件都通過,現在嘗試將執行緒數量增一,成功則退出最外層的迴圈
            if (compareAndIncrementWorkerCount(c))
                break retry;
            /失敗則重新獲取執行緒池狀態,狀態改變則從最外層迴圈開始執行,不變則從內迴圈開始執行
            c = ctl.get();  
            if (runStateOf(c) != rs)
                continue retry;
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        /構造一個Worker物件,每個Worker物件繫結一個執行緒
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int rs = runStateOf(ctl.get());
                /若執行緒池處於執行狀態或處於關閉且firstTask為null
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    執行緒提前啟動,則丟擲異常
                    if (t.isAlive()) 
                        throw new IllegalThreadStateException();
                    /將w加到Worker的集合裡
                    workers.add(w);
                    獲取Worker集合大小,若大小比largestPoolSize大小大,則更新一下
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    /新增成功
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            /若新增成功則啟動執行緒
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        /若啟動失敗(t執行緒為空或新增過程中丟擲異常)則執行addWorkerFailed方法
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
複製程式碼

Worker類

執行緒池維護的執行緒其實是一組Worker物件,Worker封裝了執行緒也繼承了AbstractQueuedSynchronizer類並實現了Runnable介面,重寫了void run()方法。至於為什麼要繼承AbstractQueuedSynchronizer類,請看下面的runWorker方法講解。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable{

    private static final long serialVersionUID = 6138294804551838833L;
    final Thread thread;
    Runnable firstTask;
    /繫結這個物件執行緒已執行完成的任務數
    volatile long completedTasks;
    
    Worker(Runnable firstTask) {
        /阻止中斷,在任務獲取前不允許中斷
        setState(-1); 
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /執行緒啟動時執行的方法
    public void run() {
        runWorker(this);
    }
    
    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(); }

    /中斷已開始執行的執行緒,這個就是為什麼要設定setState(-1)的一個原因了,這個方法會被`shutdownNow()`方法呼叫。
    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}
複製程式碼

runWorker方法

上面說到為什麼Worker類要繼承AbstractQueuedSynchronizer,其實是要用鎖的狀態來區分空閒執行緒和非空閒執行緒,在執行runWorker方法中:

  • 獲取任務時沒有加鎖(空閒狀態,可中斷執行緒)
  • 要執行任務時才加鎖(不允許中斷執行緒)

在呼叫void tryTerminate()void shutdown()這兩個方法時,會中斷空閒執行緒,所以沒有在執行任務的執行緒就可能被中斷。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); /允許中斷,與Worker建構函式的setState(-1)是一對的
    boolean completedAbruptly = true;
    try {
        /獲取到任務才進入迴圈
        while (task != null || (task = getTask()) != null) {
            /加鎖,表示非空閒狀態
            w.lock();
            /1. 如果執行緒池狀態大於等於STOP並且本執行緒未中斷,則應該執行中斷方法
             2. 或者執行Thread.interrupted()方法判斷本執行緒是否中斷並且清除中斷狀態,
                如果發現執行緒池狀態大於等於STOP則執行中斷方法。
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
                
            try {
                /ThreadPoolExecutor中的beforeExecute(wt, task)方法一個空方法,用來留給繼承ThreadPoolExecutor的類
                 來重寫該方法並在任務執行前執行
                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 {
                    /ThreadPoolExecutor中的afterExecute(task,thrown)方法也是一個空方法,用來留給繼承
                    ThreadPoolExecutor的類來重寫該方法並在任務執行後執行
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                /該執行緒執行的任務加1,即使丟擲異常
                w.completedTasks++;
                /釋放鎖,表示回到空閒狀態
                w.unlock();
            }
        }
        /執行到這一步表示是由於獲取不到任務而正常退出的,所以completedAbruptly為false
        completedAbruptly = false;
    } finally {
        /無論怎樣退出都要執行
        processWorkerExit(w, completedAbruptly);
    }
}
複製程式碼

getTask方法

private Runnable getTask() {
    /表示獲取任務是否已超時
    boolean timedOut = false; 
    
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        /1. 若執行緒池狀態大於等於停止狀態,此時執行緒池不再處理佇列的任務,並且會回收所有執行緒(不管空不空閒),
            所以此時應該把執行緒池執行緒數量減1,並且獲取的任務為空
         /2. 處於關閉狀態且任務佇列為空,表示任務佇列為空且不會有任務提交,所以執行緒數減1,並且獲取的任務為空
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);
        /是否啟用超時機制。當允許核心執行緒超時或當前執行緒數超過核心執行緒則啟用
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        /如果執行緒數量超過執行緒池所允許的最大執行緒數或者啟用超時機制情況下獲取任務超時,理論上應該回收執行緒。
         但是如果該執行緒是執行緒池中的最後一個執行緒且任務佇列不為空就可以不回收,繼續執行,要是還有其他執行緒或者任務佇列為空則回收該執行緒。
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            /嘗試將執行緒數量減1,成功返回null,失敗繼續從迴圈開始處開始。這裡為什麼不是用decrementWorkerCount()
            這種不會失敗的方法減1而採用這種方式。是因為 wc > 1,如果執行緒池不只有一個執行緒它們互相發現不只一個執行緒,
            且它們同時執行不會失敗的將執行緒數量減一的方法,到時執行緒池執行緒數量可能就為0了,哪麼佇列中的任務就沒執行緒執行了。
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            /1. 如果啟用超時機制就執行poll()方法,在keepAliveTime納秒內還沒獲取就返回null2. 如果未啟用超時機制就執行take()方法,佇列沒任務就一直阻塞直到有任務。
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            /到這裡就是因為超時獲取不到任務
            timedOut = true;
        } catch (InterruptedException retry) {
            /在執行take()過程中被中斷並不算超時
            timedOut = false;
        }
    }
}
複製程式碼

processWorkerExit方法

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    /由於不是獲取不到任務而正常退出的,得在這裡將執行緒數減1,正常退出的在getTask()方法有這個減1操作
    if (completedAbruptly) 
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    /加鎖,因為HashSet和completedTaskCount不是執行緒安全的
    mainLock.lock();
    try {
        /將執行緒執行的任務數統一加到執行緒池維護的completedTaskCount欄位
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    /嘗試將執行緒池設定為結束狀態
    tryTerminate();

    int c = ctl.get();
     /滿足當前執行緒池狀態小於STOP(執行或關閉狀態)才繼續
    if (runStateLessThan(c, STOP)) {
        若執行緒是異常退出runWorker方法就直接新增一個沒有帶初始任務的非核心執行緒
        if (!completedAbruptly) {
            /這三行程式碼找出當前執行緒池所至少存在的執行緒數
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            /如果當前執行緒數已經大於等於min,就直接返回,否則新增一個沒有帶初始任務的非核心執行緒
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}
複製程式碼

下圖是向執行緒池提交任務後,執行緒池的正常執行過程:

執行緒池正常執行過程

tryTerminate方法

執行緒池狀態轉換
terminate(結束)是執行緒池的最後一個狀態,只能由關閉或停止狀態轉變為結束狀態。

final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
         /如果滿足下面任意一個條件就沒辦法到達結束狀態
         1. 執行緒池處於執行狀態
         2. 執行緒池狀態是TIDYING或已經是結束狀態
         3. 執行緒池處於關閉狀態且任務佇列不為空
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
         /當前執行緒數量不為0也無法到達結束狀態
        if (workerCountOf(c) != 0) { 
            /中斷一個空閒執行緒
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
             /嘗試將執行緒池狀態設定為TIDYING,失敗重迴圈開始處開始
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    /terminated()是一個空方法,留給繼承ThreadPoolExecutor的類覆蓋
                    terminated();
                } finally {
                    /嘗試將執行緒池狀態設定為TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        
    }
}
複製程式碼

關閉操作

我們可以通過呼叫void shutdown()方法關閉執行緒池,關閉後執行緒池後不允許接受新任務

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        / 安全策略判斷
        checkShutdownAccess();
        /設定執行緒池狀態為SHUTDOWN狀態
        advanceRunState(SHUTDOWN);
        /中斷所有空閒執行緒
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    /嘗試結束執行緒池
    tryTerminate();
}
複製程式碼

停止操作

我們可以在執行和關閉狀態下通過呼叫void shutdownNow()方法停止執行緒池,停止後執行緒池後不允許接受新任務,也不會執行阻塞佇列裡的任務,還會中斷當前所有的執行緒。

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        / 安全策略判斷  
        checkShutdownAccess();
        /設定執行緒池狀態為STOP狀態
        advanceRunState(STOP);
        /中斷所有執行緒,不管是空閒還是非空閒
        interruptWorkers();
        /取出阻塞佇列的所有任務
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    /嘗試結束執行緒池
    tryTerminate();
    return tasks;
}
複製程式碼

3. 執行緒池的配置

Executors提供了四種靜態工廠方法來建立四種不同配置的執行緒池:

  • newFixedThreadPool(int nThreads)

    接受一個int型別的nThreads變數,建立一個核心執行緒數最大執行緒數都為nThreads的執行緒池(即最大執行緒數為nThreads),且使用一個無界的阻塞佇列LinkedBlockingQueue。如果不設定核心執行緒超時的話,建立的執行緒是不會超時的。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
複製程式碼
  • newSingleThreadExecutor()

    建立一個核心執行緒數最大執行緒數都為1的執行緒池(即最大執行緒數為1),且使用一個無界的阻塞佇列LinkedBlockingQueue,不設定核心執行緒超時的話,建立的執行緒也是不會超時的。唯一執行緒可以保證任務的順序執行,如果這個唯一的執行緒執行過程中因為異常而結束的話,在processWorkerExit方法最後會判斷是否因異常而結束而建立一個新執行緒繼續執行。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
複製程式碼
  • newCachedThreadPool()

    建立一個核心執行緒數為0,最大執行緒數Integer.MAX_VALUE的執行緒池,超時時間為60秒,所以執行緒空閒時間超過60秒就會被回收。使用了一個同步佇列作為阻塞佇列,同步佇列不儲存元素,且在一端進行插入,另一端要有移除操作插入才會成功,否則插入操作會阻塞等待。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
複製程式碼
  • newScheduledThreadPool()

    建立一個核心執行緒數為corePoolSize的執行緒池,用於指定的時間內週期性的執行所的任務。ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
複製程式碼

相關文章