前言
在我們進行開發的時候,為了充分利用系統資源,我們通常會進行多線程開發,實現起來非常簡單,需要使用線程的時候就去創建一個線程(繼承Thread類、實現Runnable接口、使用Callable和Future),但是這樣也有一點問題,就是如果並發的線程數量很多,創建線程、銷毀線程都是需要消耗時間、資源,這個時候線程池就派上用場了
一、四種線程池的介紹
Java通過Executors提供了四種線程池,分別是
1.newSingleThreadExecutor()
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務都是按照指定的順序(FIFO,LIFO,優先級)執行
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
2.newFixedThreadExecutor()
創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
3.newCachedThreadPool()
創建一個可緩存的線程池,如果當前沒有可用線程,在執行結束后緩存60s,如果不被調用則移除線程。調用execute()方法時可以重用緩存中的線程。適用於很多短期異步任務的環境,可以提高程序性能。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
4.newScheduledThreadPool()(在ScheduleThreadPoolExecutor類中,ThreadPoolExecutor的子類)
創建一個定長線程池,支持定時及周期性任務執行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
線程池的使用
使用方式一(不提倡我們直接使用ThreadPoolExecutor,而是使用Executors類中提供的幾個靜態方法來創建線程池):
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5)); threadPoolExecutor.execute(new Runnable() { @Override public void run() { } });
使用方式二:
ExecutorService executorService1 = Executors.newFixedThreadPool(3); ExecutorService executorService2 = Executors.newSingleThreadExecutor(); ExecutorService executorService3 = Executors.newCachedThreadPool(); ExecutorService executorService4 = Executors.newScheduledThreadPool(3); executorService1.execute(new Runnable() { @Override public void run() { } });
ExecutorService是真正的線程池接口,所以我們在通過Executors創建各種線程時,都是采用上述代碼所示的方式
二、線程池的底層類與接口
在介紹線程池的實現機制之前,先了解一下線程池重要的類或接口
ExecutorService是真正的線程池接口
Executors是靜態工廠的功能,生產各種類型線程池
Executor是線程池的頂級接口,只是一個執行線程的工具,只提供一個execute(Runnable command)的方法,真正的線程池接口是ExecutorService
AbstractExecutorService實現了ExecutorService接口,實現了其中大部分的方法(有沒有實現的方法,所以被聲明為Abstract)
ThreadPoolExecutor,繼承了AbstractExecutorService,是ExecutorService的默認實現
1、ThreadPoolExecutor類
(1)構造函數
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類,因此如果要透徹地了解Java中的線程池,必須先了解這個類。
在ThreadPoolExecutor類中提供了四個構造方法(前面三個構造器都是調用第四個構造器進行的初始化工作):
public class ThreadPoolExecutor extends AbstractExecutorService { ..... public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); ... }
構造器中各個參數的含義:
- corePoolSize:核心池的大小,這個參數跟后面講述的線程池的實現原理有非常大的關系。在創建了線程池后,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池后,線程池中的線程數為0,當有任務來之后,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize后,就會把到達的任務放到緩存隊列當中;
- maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程
- keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程空閑的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數為0;
- unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:
TimeUnit.DAYS; //天 TimeUnit.HOURS; //小時 TimeUnit.MINUTES; //分鍾 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //納秒
- workQueue:一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這里的阻塞隊列有以下幾種選擇:ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。
- threadFactory:線程工廠,主要用來創建線程;
- handler:表示當拒絕處理任務時的策略,有以下四種取值:
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程) ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
(2)ThreadPoolExecutor方法
execute()
submit()
shutdown()
shutdownNow()
getQueue()
getPoolSize()
getActiveCount()
getCompletedTaskCount()
execute()方法實際上是Executor中聲明的方法,在ThreadPoolExecutor進行了具體的實現,這個方法是ThreadPoolExecutor的核心方法,通過這個方法可以向線程池提交一個任務,交由線程池去執行。
submit()方法是在ExecutorService中聲明的方法,在AbstractExecutorService就已經有了具體的實現,在ThreadPoolExecutor中並沒有對其進行重寫,這個方法也是用來向線程池提交任務的,但是它和execute()方法不同,它能夠返回任務執行的結果,去看submit()方法的實現,會發現它實際上還是調用的execute()方法,只不過它利用了Future來獲取任務執行結果(Future相關內容將在下一篇講述)。
shutdown()和shutdownNow()是用來關閉線程池的。
getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等獲取與線程池相關屬性的方法
還有在下文提到的runwork()、addwork()、processworkerExit() 方法等等
2、AbstractExecutorService類
下面我們看一下AbstractExecutorService的具體方法
public abstract class AbstractExecutorService implements ExecutorService {
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {...}; protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {...}; public Future<?> submit(Runnable task) {}; public <T> Future<T> submit(Runnable task, T result) {...}; public <T> Future<T> submit(Callable<T> task) {...}; private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException {...}; public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {...}; public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {...}; public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {...}; public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {...}; }
AbstractExecutorService是一個抽象類,它實現了ExecutorService接口。
3、ExecutorService接口
public interface ExecutorService extends Executor { void shutdown(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
而ExecutorService又是繼承了Executor接口,我們看一下Executor接口的實現:
4、Executor接口
public interface Executor { void execute(Runnable command); }
上述類與接口之間的關系
Executor是一個頂層接口,在它里面只聲明了一個方法execute(Runnable),返回值為void,參數為Runnable類型,從字面意思可以理解,就是用來執行傳進去的任務的;
然后ExecutorService接口繼承了Executor接口,並聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象類AbstractExecutorService實現了ExecutorService接口,基本實現了ExecutorService中聲明的所有方法;
然后ThreadPoolExecutor繼承了類AbstractExecutorService。
三、線程池的底層實現原理
1、線程池的狀態
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits 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; // Packing and unpacking ctl 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; }
其中ctl這個AtomicInteger的功能很強大,其高3位用於維護線程池運行狀態,低29位維護線程池中線程數量
1、RUNNING:-1<<COUNT_BITS,即高3位為1,低29位為0,該狀態的線程池會接收新任務,也會處理在阻塞隊列中等待處理的任務
2、SHUTDOWN:0<<COUNT_BITS,即高3位為0,低29位為0,該狀態的線程池不會再接收新任務,但還會處理已經提交到阻塞隊列中等待處理的任務
3、STOP:1<<COUNT_BITS,即高3位為001,低29位為0,該狀態的線程池不會再接收新任務,不會處理在阻塞隊列中等待的任務,而且還會中斷正在運行的任務
4、TIDYING:2<<COUNT_BITS,即高3位為010,低29位為0,所有任務都被終止了,workerCount為0,為此狀態時還將調用terminated()方法
5、TERMINATED:3<<COUNT_BITS,即高3位為100,低29位為0,terminated()方法調用完成后變成此狀態
這些狀態均由int型表示,大小關系為 RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED,這個順序基本上也是遵循線程池從 運行 到 終止這個過程。
runStateOf(int c) 方法:c & 高3位為1,低29位為0的~CAPACITY,用於獲取高3位保存的線程池狀態
workerCountOf(int c)方法:c & 高3位為0,低29位為1的CAPACITY,用於獲取低29位的線程數量
ctlOf(int rs, int wc)方法:參數rs表示runState,參數wc表示workerCount,即根據runState和workerCount打包合並成ctl
2、執行過程
一個任務從提交到執行完畢經歷過程如下:
第一步:如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;
第二步:如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閑線程將其取出去執行;若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務;
第三步:如果線程池中的線程數量大於等於corePoolSize,且隊列workQueue已滿,但線程池中的線程數量小於maximumPoolSize,則會創建新的線程來處理被添加的任務
第四步:如果當前線程池中的線程數目達到maximumPoolSize,則會采取任務拒絕策略進行處理;
3、任務拒絕策略
當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略,通常有以下四種策略:
- ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
- ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
- ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程)
- ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
4、在執行過程中所涉及到的方法
(1)execute(Runnable command)(提交任務)
在ThreadPoolExecutor類中,最核心的任務提交方法是execute()方法,雖然通過submit也可以提交任務,但是實際上submit方法里面最終調用的還是execute()方法,所以我們只需要研究execute()方法的實現
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)) reject(command); }
源碼解析:
1、如果線程池當前線程數量少於corePoolSize,則addWorker(command, true)創建新worker線程,如創建成功返回,如沒創建成功,則執行后續步驟;
addWorker(command, true)失敗的原因可能是:
A、線程池已經shutdown,shutdown的線程池不再接收新任務
B、workerCountOf(c) < corePoolSize 判斷后,由於並發,別的線程先創建了worker線程,導致workerCount>=corePoolSize
2、如果線程池還在running狀態,將task加入workQueue阻塞隊列中,如果加入成功,進行double-check,如果加入失敗(可能是隊列已滿),則執行后續步驟;
double-check主要目的是判斷剛加入workQueue阻塞隊列的task是否能被執行
A、如果線程池已經不是running狀態了,應該拒絕添加新任務,從workQueue中刪除任務
B、如果線程池是運行狀態,或者從workQueue中刪除任務失敗(剛好有一個線程執行完畢,並消耗了這個任務),確保還有線程執行任務(只要有一個就夠了)
3、如果線程池不是running狀態 或者 無法入隊列,嘗試開啟新線程,擴容至maxPoolSize,如果addWork(command, false)失敗了,拒絕當前command
(2)addwork(Runnable firstTask,boolean core)(添加任務)
參數說明:
firstTask:worker線程的初始任務,可以為空
core:true(將corePoolSize作為上限),false(將maximumPoolSize作為上限)
addWorker方法有4種傳參的方式:
1、addWorker(command, true)
2、addWorker(command, false)
3、addWorker(null, false)
4、addWorker(null, true)
在execute方法中就使用了前3種,結合這個核心方法進行以下分析
第一個:線程數小於corePoolSize時,放一個需要處理的task進Workers Set。如果Workers Set長度超過corePoolSize,就返回false
第二個:當隊列被放滿時,就嘗試將這個新來的task直接放入Workers Set,而此時Workers Set的長度限制是maximumPoolSize。如果線程池也滿了的話就返回false
第三個:放入一個空的task進workers Set,長度限制是maximumPoolSize。這樣一個task為空的worker在線程執行的時候會去任務隊列里拿任務,這樣就相當於創建了一個新的線程,只是沒有馬上分配任務
第四個:這個方法就是放一個null的task進Workers Set,而且是在小於corePoolSize時,如果此時Set中的數量已經達到corePoolSize那就返回false,什么也不干。實際使用中是在prestartAllCoreThreads()方法,這個方法用來為線程池預先啟動corePoolSize個worker等待從workQueue中獲取任務執行
源碼解析:
1、判斷線程池當前是否為可以添加worker線程的狀態,可以則繼續下一步,不可以return false:
A、線程池狀態>shutdown,可能為stop、tidying、terminated,不能添加worker線程
B、線程池狀態==shutdown,firstTask不為空,不能添加worker線程,因為shutdown狀態的線程池不接收新任務
C、線程池狀態==shutdown,firstTask==null,workQueue為空,不能添加worker線程,因為firstTask為空是為了添加一個沒有任務的線程再從workQueue獲取task,而workQueue為空,說明添加無任務線程已經沒有意義
2、線程池當前線程數量是否超過上限(corePoolSize 或 maximumPoolSize),超過了return false,沒超過則對workerCount+1,繼續下一步
3、在線程池的ReentrantLock保證下,向Workers Set中添加新創建的worker實例,添加完成后解鎖,並啟動worker線程,如果這一切都成功了,return true,如果添加worker入Set失敗或啟動失敗,調用addWorkerFailed()邏輯
其中,線程池會把每個線程封裝成一個Worker對象,由addWorker(Runnable firstTask, boolean core)方法控制,firstTask代表線程池首要執行的任務,core代表是否使用corePoolSize參數作為線程池最大標記。
(3)內部類worker
Worker類本身既實現了Runnable,又繼承了AbstractQueuedSynchronizer(以下簡稱AQS),所以其既是一個可執行的任務,又可以達到鎖的效果
new Worker()
1、將AQS的state置為-1,在runWoker()前不允許中斷
2、待執行的任務會以參數傳入,並賦予firstTask
3、用Worker這個Runnable創建Thread
之所以Worker自己實現Runnable,並創建Thread,在firstTask外包一層,是因為要通過Worker控制中斷,而firstTask這個工作任務只是負責執行業務
Worker控制中斷主要有以下幾方面:
1、初始AQS狀態為-1,此時不允許中斷interrupt(),只有在worker線程啟動了,執行了runWoker(),將state置為0,才能中斷
不允許中斷體現在:
A、shutdown()線程池時,會對每個worker tryLock()上鎖,而Worker類這個AQS的tryAcquire()方法是固定將state從0->1,故初始狀態state==-1時tryLock()失敗,沒發interrupt()
B、shutdownNow()線程池時,不用tryLock()上鎖,但調用worker.interruptIfStarted()終止worker,interruptIfStarted()也有state>0才能interrupt的邏輯
2、為了防止某種情況下,在運行中的worker被中斷,runWorker()每次運行任務時都會lock()上鎖,而shutdown()這類可能會終止worker的操作需要先獲取worker的鎖,這樣就防止了中斷正在運行的線程
Worker實現的AQS為不可重入鎖,為了是在獲得worker鎖的情況下再進入其它一些需要加鎖的方法
Worker和Task的區別:
Worker是線程池中的線程,而Task雖然是runnable,但是並沒有真正執行,只是被Worker調用了run方法,后面會看到這部分的實現。
(4)runwork(Worker w)(執行任務)
1、Worker線程啟動后,通過Worker類的run()方法調用runWorker(this)
2、執行任務之前,首先worker.unlock(),將AQS的state置為0,允許中斷當前worker線程
3、開始執行firstTask,調用task.run(),在執行任務前會上鎖wroker.lock(),在執行完任務后會解鎖,為了防止在任務運行時被線程池一些中斷操作中斷
4、在任務執行前后,可以根據業務場景自定義beforeExecute() 和 afterExecute()方法
5、無論在beforeExecute()、task.run()、afterExecute()發生異常上拋,都會導致worker線程終止,進入processWorkerExit()處理worker退出的流程
6、如正常執行完當前task后,會通過getTask()從阻塞隊列中獲取新任務,當隊列中沒有任務,且獲取任務超時,那么當前worker也會進入退出流程
(5)getTask()(獲取任務)
1、首先判斷是否可以滿足從workQueue中獲取任務的條件,不滿足return null
A、線程池狀態是否滿足:
(a)shutdown狀態 + workQueue為空 或 stop狀態,都不滿足,因為被shutdown后還是要執行workQueue剩余的任務,但workQueue也為空,就可以退出了
(b)stop狀態,shutdownNow()操作會使線程池進入stop,此時不接受新任務,中斷正在執行的任務,workQueue中的任務也不執行了,故return null返回
B、線程數量是否超過maximumPoolSize 或 獲取任務是否超時
(a)線程數量超過maximumPoolSize可能是線程池在運行時被調用了setMaximumPoolSize()被改變了大小,否則已經addWorker()成功不會超過maximumPoolSize
(b)如果 當前線程數量>corePoolSize,才會檢查是否獲取任務超時,這也體現了當線程數量達到maximumPoolSize后,如果一直沒有新任務,會逐漸終止worker線程直到corePoolSize
2、如果滿足獲取任務條件,根據是否需要定時獲取調用不同方法:
A、workQueue.poll():如果在keepAliveTime時間內,阻塞隊列還是沒有任務,返回null
B、workQueue.take():如果阻塞隊列為空,當前線程會被掛起等待;當隊列中有任務加入時,線程被喚醒,take方法返回任務
3、在阻塞從workQueue中獲取任務時,可以被interrupt()中斷,代碼中捕獲了InterruptedException,重置timedOut為初始值false,再次執行第1步中的判斷,滿足就繼續獲取任務,不滿足return null,會進入worker退出的流程
(6)processWorkerExit(Worker w,boolean completedAbruptly)(worker線程退出)
參數:
worker: 要結束的worker
completedAbruptly: 是否突然完成(是否因為異常退出)
執行流程:
1、worker數量-1
A、如果是突然終止,說明是task執行時異常情況導致,即run()方法執行時發生了異常,那么正在工作的worker線程數量需要-1
B、如果不是突然終止,說明是worker線程沒有task可執行了,不用-1,因為已經在getTask()方法中-1了
2、從Workers Set中移除worker,刪除時需要上鎖mainlock
3、tryTerminate():在對線程池有負效益的操作時,都需要“嘗試終止”線程池,大概邏輯:
判斷線程池是否滿足終止的狀態
A、如果狀態滿足,但還有線程池還有線程,嘗試對其發出中斷響應,使其能進入退出流程
B、沒有線程了,更新狀態為tidying->terminated
4、是否需要增加worker線程,如果線程池還沒有完全終止,仍需要保持一定數量的線程
線程池狀態是running 或 shutdown
A、如果當前線程是突然終止的,addWorker()
B、如果當前線程不是突然終止的,但當前線程數量 < 要維護的線程數量,addWorker()
故如果調用線程池shutdown(),直到workQueue為空前,線程池都會維持corePoolSize個線程,然后再逐漸銷毀這corePoolSize個線程
以上就是我所總結的對於java線程池的理解,其中有部分源碼並沒有粘貼過來,大家可以查看JDK源碼,有什么問題歡迎大家在下面評論,交流一下