1、為什么不應(yīng)該自動(dòng)創(chuàng)建線程池?
所謂的自動(dòng)創(chuàng)建線程池就是直接調(diào)用 Executors 的各種方法來生成常見的線程池,例如 Executors.newCachedThreadPool()。但這樣做是有一定風(fēng)險(xiǎn)的,接下來就來逐一分析自動(dòng)創(chuàng)建線程池可能帶來哪些問題。
FixedThreadPool
首先來看第一種線程池 FixedThreadPool, 它是線程數(shù)量固定的線程池,如源碼所示,newFixedThreadPool 內(nèi)部實(shí)際還是調(diào)用了 ThreadPoolExecutor 構(gòu)造函數(shù)。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L,
TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
通過往構(gòu)造函數(shù)中傳參,創(chuàng)建了一個(gè)核心線程數(shù)和最大線程數(shù)相等的線程池,它們的數(shù)量也就是傳入的參數(shù),這里的重點(diǎn)是使用的隊(duì)列是容量沒有上限的 LinkedBlockingQueue,如果對(duì)任務(wù)的處理速度比較慢,那么隨著請(qǐng)求的增多,隊(duì)列中堆積的任務(wù)也會(huì)越來越多,最終大量堆積的任務(wù)會(huì)占用大量?jī)?nèi)存,并發(fā)生 OOM ,也就是OutOfMemoryError,這幾乎會(huì)影響到整個(gè)程序,會(huì)造成很嚴(yán)重的后果。
SingleThreadExecutor
第二種線程池是 SingleThreadExecutor,來分析下創(chuàng)建它的源碼。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1,0L,
TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
可以看出,newSingleThreadExecutor 和 newFixedThreadPool 的原理是一樣的,只不過把核心線程數(shù)和最大線程數(shù)都直接設(shè)置成了 1,但是任務(wù)隊(duì)列仍是無界的 LinkedBlockingQueue,所以也會(huì)導(dǎo)致同樣的問題,也就是當(dāng)任務(wù)堆積時(shí),可能會(huì)占用大量的內(nèi)存并導(dǎo)致 OOM。
CachedThreadPool
第三種線程池是 CachedThreadPool,創(chuàng)建它的源碼下所示。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
這里的 CachedThreadPool 和前面兩種線程池不一樣的地方在于任務(wù)隊(duì)列使用的是 SynchronousQueue,SynchronousQueue 本身并不存儲(chǔ)任務(wù),而是對(duì)任務(wù)直接進(jìn)行轉(zhuǎn)發(fā),這本身是沒有問題的,但會(huì)發(fā)現(xiàn)構(gòu)造函數(shù)的第二個(gè)參數(shù)被設(shè)置成了 Integer.MAX_VALUE,這個(gè)參數(shù)的含義是最大線程數(shù),所以由于 CachedThreadPool 并不限制線程的數(shù)量,當(dāng)任務(wù)數(shù)量特別多的時(shí)候,就可能會(huì)導(dǎo)致創(chuàng)建非常多的線程,最終超過了操作系統(tǒng)的上限而無法創(chuàng)建新線程,或者導(dǎo)致內(nèi)存不足。
ScheduledThreadPool 和 SingleThreadScheduledExecutor
第四種線程池 ScheduledThreadPool 和第五種線程池 SingleThreadScheduledExecutor 的原理是一樣的,創(chuàng)建 ScheduledThreadPool 的源碼如下所示。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
而這里的 ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的子類,調(diào)用的它的構(gòu)造方法如下所示。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}
通過源碼可以看出,它采用的任務(wù)隊(duì)列是 DelayedWorkQueue,這是一個(gè)延遲隊(duì)列,同時(shí)也是一個(gè)無界隊(duì)列,所以和 LinkedBlockingQueue 一樣,如果隊(duì)列中存放過的任務(wù),就可能導(dǎo)致 OOM。
這幾種自動(dòng)創(chuàng)建的線程池都存在風(fēng)險(xiǎn),相比較而言,自己手動(dòng)創(chuàng)建會(huì)更好,因?yàn)槲覀兛梢愿用鞔_線程池的運(yùn)行規(guī)則,不僅可以選擇適合自己的線程數(shù)量,更可以在必要的時(shí)候拒絕新任務(wù)的提交,避免資源耗盡的風(fēng)險(xiǎn)。