提示:若有疑問請私信聯繫、下方有源代碼地址,請自行拿取git
提示:如下是本篇文章正文內容,下面案例可供參考markdown
參數名稱 | 參數類型 | 參數含義 |
---|---|---|
corePoolSize | int | 核心線程池大小 |
maximumPoolSize | int | 最大線程池大小 |
keepAliveTime | long | 線程最大空閒時間 |
unit | TimeUnit | 時間單位 |
workQueue | BlockingQueue | 線程等待隊列 |
threadFactory | ThreadFactory | 線程建立工廠 |
handler | RejectedExecutionHandler | 拒絕策略 |
咱們看下ThreadPoolExecutor類的execute方法底層源碼進行分析 | ||
![]() |
||
OK,根據判斷可知: |
1.若是正在運行的線程少於corePoolSize線程,請嘗試使用給定命令做爲其第一個任務啓動一個新線程。多線程
2.若是任務能夠成功排隊,那麼咱們仍然須要再次檢查是否應該添加線程(由於現有線程自上次檢查後就死掉了),或者自進入此方法後該池已關閉。所以,咱們從新檢查狀態,並在必要時回滾排隊,若是中止,或者若是沒有線程,則啓動一個新線程。併發
3.若是咱們沒法將任務排隊,則嘗試添加一個新線程。若是失敗,咱們知道咱們已關閉或處於飽和狀態,所以拒絕該任務。函數
代碼以下(示例):高併發
@Test public void testNewSingleThreadExecutor() {
ExecutorService threaPool = Executors.newSingleThreadExecutor();
long start = System.currentTimeMillis();
System.out.println("線程池執行開始");
int idx = 10;
while (--idx > 0) {
threaPool.execute(() -> {
try {
LOGGER.info("線程執行中");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
});
}
threaPool.shutdown();
for (; ; ) {
if (threaPool.isTerminated())
break;
}
long end = System.currentTimeMillis();
System.out.println("線程池執行結束,總用時:" + (end - start) + " ms ");
}
複製代碼
此測試方法運行的結果以下: 注意看我用紅框標記的地方,只採用了1個線程去執行,原理是什麼呢?讓咱們看看newSingleThreadExecutor的源碼oop
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
複製代碼
構建了ThreadPoolExecutor線程池,核心線程1個,最大執行線程1個,等待隊列是LinkedBlockingQueue,我們再點進去看看LinkedBlockingQueue默認構造函數是啥
能夠看到這是默認時一個容量爲Interger.MAX_VALUE的隊列源碼分析
結論:newSingleThreadExecutor是一個核心線程爲1,線程池中容許最大線程爲1,等待隊列爲無限大的線程池,因此你應該知道爲何它只開了一個線程去執行了。單元測試
代碼以下(示例):測試
@Test public void testNewFixedThreadPool() {
ExecutorService threaPool = Executors.newFixedThreadPool(5);
long start = System.currentTimeMillis();
System.out.println("線程池執行開始");
int idx = 20;
while (--idx >= 0) {
threaPool.execute(() -> {
try {
LOGGER.info("線程執行中");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
});
}
threaPool.shutdown();
for (; ; ) {
if (threaPool.isTerminated())
break;
}
long end = System.currentTimeMillis();
System.out.println("線程池執行結束,總用時:" + (end - start) + " ms ");
}
複製代碼
先來看下執行結果
OK,看下執行結果可知,只開啓了5個線程,每次批量的執行5個,接下來我們看看它的源碼
也一樣的構造了ThreadPoolExecutor線程池,參數爲:核心線程數、線程池最大線程數都爲傳入的參數,單元測試傳的是5,因此開5個線程運行,運行完重複使用這5個線程去執行隊列中的。
結論:newFixedThreadPool是一個根據傳入參數來執行固定大小的線程池
代碼以下(示例):
@Test public void testNewCachedThreadPool() {
ExecutorService threaPool = Executors.newCachedThreadPool();
long start = System.currentTimeMillis();
System.out.println("線程池執行開始");
int idx = 200;
while (--idx >= 0) {
threaPool.execute(() -> {
LOGGER.info("線程執行中");
});
}
threaPool.shutdown();
for (; ; ) {
if (threaPool.isTerminated())
break;
}
long end = System.currentTimeMillis();
System.out.println("線程池執行結束,總用時:" + (end - start) + " ms ");
}
複製代碼
OK,這裏跟上面不一樣,我們執行200個線程,咋們先看執行結果, 很明顯能夠看到跟上面的不一樣,在執行時間很短的任務時重複的利用線程去執行,緣由是什麼呢?我們先看源碼
建立了一個核心線程數爲0,最大執行線程爲Interger.MAX_VALUE,而且注意這裏用了SynchronousQueue這個隊列,SynchronousQueue沒有容量,是無緩衝等待隊列,是一個不存儲元素的阻塞隊列,會直接將任務交給消費者,必須等隊列中的添加元素被消費後才能繼續添加新的元素。
SynchronousQueue,至於它的底層原理後期會寫一篇專門關於隊列的文章,這裏再也不細說
結論:newCachedThreadPool它是一個能夠無限擴大的線程池,當前沒有空閒線程時它會建立一個新的線程,若是有空閒線程會使用空閒線程處理
經過以上的測試案例與源碼分析,相信你們對線程池有了必定的認識,總結以下:
1.newSingleThreadExecutor:只開啓一個線程運行,處理效率較慢,阻塞隊列大小是沒有大小限制的,若是隊列堆積數據太多會形成資源消耗
2.newFixedThreadPool:一個固定大小的線程池,可控制線程併發數量,但阻塞隊列大小是沒有大小限制的,若是隊列堆積數據太多會形成資源消耗
3.newCachedThreadPool:比較適合處理執行時間較短的業務,但線程如果無限制的建立,可能會致使內存佔用過多而產生OOM,而且會形成cpu過分切換消耗太多資源。
因此使用推薦是根據業務場景實現自定義ThreadPoolExecutor,特別是高併發大流量系統,這也是爲何阿里內部不推薦使用以上幾種線程池的緣由。
是否是感受很簡單?更多用法請點擊下方查看源碼,關注我帶你揭祕更多高級用法
源碼地址:點此查看源碼.