1. 線程池的作用:
- 重復利用已經(jīng)創(chuàng)建好的線程, 降低創(chuàng)建線程和銷毀線程的性能開銷
- 合理的設(shè)置線程池大小可以避免因為線程數(shù)超出硬件資源瓶頸帶來的問題,類似起到了限流的作用
2. 線程池的創(chuàng)建
2.1 原生線程池 ThreadPoolExecutor(推薦)
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

2.2 JDK工具類 Executors(不推薦)
1. newFixedThreadPool
工作線程控制在固定的數(shù)量上,但任務(wù)隊列是無界的
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2 newCachedThreadPool
最大線程數(shù)無上限
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3 使用注意事項
3.1 不要使用Executors提供的工具方法創(chuàng)建線程池,會導致OOM,原因已在上面分析
3.2 線程池參數(shù)的配置
1. corePoolSize、maximumPoolSize 的選擇
因為實際很難界定系統(tǒng)是IO密集型還是CPU密集型,并且tasks、taskcost也不是一成不變的,所以實際使用中先根據(jù)經(jīng)驗估算出一個經(jīng)驗值,然后再通過壓測驗證經(jīng)驗值, 這時如果能直觀的觀察到線程池的內(nèi)部狀態(tài)就非常有必要了
| 參數(shù) | 備注 |
|---|---|
| 根據(jù)任務(wù)類型估算: CPU密集型: N+1 IO密集型: corePoolSize 1, maximumPoolSize: 2N |
《Java多線程變成實戰(zhàn)指南》 IO corePoolSize 為1, 是為了減少IO操作引起的上下文切換 |
| 根據(jù)tasks、taskcost、responsetime估算 | https://www.lagou.com/lgeduarticle/18336.html |
| 動態(tài)修改線程池配置 | https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html |
1.2 BlockingQueue的選擇
通常認為LinkedBlockingQueue吞吐量會比ArrayBlockingQueueu高[http://www.itdecent.cn/p/5b85c1794351]
i. ArrayBlockingQueue: 存儲空間是預先分配的, 一把全局鎖
ii. LinkedBlockingQueue: 存取兩把鎖,用于存儲隊列元素的存儲空間是在其使用過程中動態(tài)分配的,因此它可能會增加JVM垃圾回收的負擔。
1.3 拒絕策略的選擇

ThreadPoolExecutor的擴展
因為ThreadPoolExecutor為了通用,只有在阻塞隊列滿的時候,才繼續(xù)創(chuàng)建線程,那有沒有辦法corePoolSize達到時,繼續(xù)創(chuàng)建線程,直到達到了maximumPoolSize才放到阻塞隊列中,可以參考唯品會開源的QueuableCachedThreadPool
3.3 線程池的監(jiān)控
線程池監(jiān)控主要為以下幾方面提供幫助:
1.為線程池調(diào)優(yōu)提供參考
1.幫助定位問題以及監(jiān)控報警
ThreadPoolExecutor提供一些方法暴露內(nèi)部狀態(tài),可以將指標記錄到監(jiān)控中
private void doMetric() {
// 當前線程池中運行的線程總數(shù)
MetricClient.record("thread.pool.pool.size", threadPoolExecutor.getPoolSize());
// 歷史峰值線程數(shù)
MetricClient.record("thread.pool.largest.pool.size", threadPoolExecutor.getLargestPoolSize());
// 當前任務(wù)隊列中積壓任務(wù)的總數(shù)
MetricClient.record("thread.pool.queue.size", threadPoolExecutor.getQueue().size());
// 當前活躍線程數(shù)
MetricClient.record("thread.pool.active.size", threadPoolExecutor.getActiveCount());
}
3.4 ThreadPoolExecutor需要在全局聲明
否則會重復創(chuàng)建多個線程池, 而且核心線程池不會被銷毀, 一個反例如下
public static void main(String[] args) throws Exception {
FixedThreadPool fixedThreadPool = new FixedThreadPool();
for (int i = 0; i < 5; i++) {
fixedThreadPool.test();
}
TimeUnit.SECONDS.sleep(100);
System.out.println("Finished");
}
private void test() {
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
1, 10,
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(5),
new ThreadFactoryBuilder().setNameFormat("threadPool-%d").build());
threadPoolExecutor.submit(() -> System.out.println(Thread.currentThread().getName()));
}
因為調(diào)用test函數(shù)5次,最終會有5個線程,且狀態(tài)為WAIT。

3.5 避免線程泄露
線程泄露是指線程池中的工作者意外終止,使得線程池中實際可用的工作者線程變少
如等待網(wǎng)絡(luò)I/O, 而該任務(wù)有沒有對這個等待指定時間限制,如果外部資源一直沒有返回該任務(wù)所等待的結(jié)果,就會導致該線程一直處于等待狀態(tài)而無法執(zhí)行其他任務(wù)。
3.6 線程池隔離
- 1 避免相互影響
應(yīng)用中通常配置多個線程池,要根據(jù)任務(wù)的“輕重緩急”來指定線程池的核心參數(shù),包括線程數(shù)、回收策略和任務(wù)隊列,避免相互之間影響。 - 1 避免引發(fā)死鎖
提交給同一線程池實例執(zhí)行的任務(wù)是相互獨立的,而不是彼此有依賴關(guān)系的任務(wù)
如下是一個引起死鎖的極端例子, 任務(wù)A依賴任務(wù)B的執(zhí)行結(jié)果,等待B的執(zhí)行,任務(wù)B因為資源限制,位于阻塞隊列中,等待任務(wù)A執(zhí)行結(jié)束,造成死鎖
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
Runnable taskA = () -> {
System.out.println("Task A start.");
Runnable taskB = () -> System.out.println("Process Task B");
Future result = threadPoolExecutor.submit(taskB);
try {
result.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Task A finished");
};
Future future = threadPoolExecutor.submit(taskA);
future.get();
參考文獻:
- Java多線程編程實戰(zhàn)指南
- Java線程池實現(xiàn)原理及其在美團業(yè)務(wù)中的實踐
- ArrayBlockingQueue與LinkedBlockingQueue