什么是ThreadPoolExecutor?
JDK1.5開始出現(xiàn),繼承關(guān)系如下:
ThreadPoolExecutor <- AbstractExecutorService <-ExecutorService <- Executor
后面兩個(gè)是接口,官方注釋表明主要是解決兩個(gè)問題:
1.當(dāng)執(zhí)行大量異步任務(wù)時(shí)候線程池能夠提供較好的性能,這是因?yàn)槭褂镁€程池可以使每個(gè)任務(wù)的調(diào)用開銷減少。
2.線程池提供了一種資源限制和管理的手段,比如當(dāng)執(zhí)行一系列任務(wù)時(shí)候?qū)€程的管理,每個(gè)ThreadPoolExecutor也保留了一些基本的統(tǒng)計(jì)數(shù)據(jù),比如當(dāng)前線程池完成的任務(wù)數(shù)目。
為什么系統(tǒng)會(huì)給我們提供線程池呢?
新建線程的缺點(diǎn)如下:
a. 每次new Thread新建對象性能差。
b. 線程缺乏統(tǒng)一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統(tǒng)資源導(dǎo)致死機(jī)或oom。
c. 缺乏更多功能,如定時(shí)執(zhí)行、定期執(zhí)行、線程中斷。
相比new Thread,四種線程池的好處在于:
a. 重用存在的線程,減少對象創(chuàng)建、消亡的開銷,性能佳。
b. 可有效控制最大并發(fā)線程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過多資源競爭,避免堵塞。
c. 提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。
如何使用ThreadPoolExecutor
1.使用Executors工具類(讓我想到了Collections)里面已有的靜態(tài)方法創(chuàng)建
val newFixedThreadPool = Executors.newFixedThreadPool(20)
val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
val newCachedThreadPool = Executors.newCachedThreadPool()
val newScheduledThreadPool = Executors.newScheduledThreadPool(20)
2.ThreadPoolExecutort提供了四個(gè)構(gòu)造方法,我們可以直接創(chuàng)建自定義的線程池,方法1 本質(zhì)上也是通過ThreadPoolExecutort創(chuàng)建的,只是幫我們封裝好了。
相關(guān)參數(shù)解釋如下:
序號(hào) | 參數(shù)名 | 參數(shù)類型 | 參數(shù)含義 |
---|---|---|---|
1 | corePoolSize | int | 核心線程池大小 |
2 | maximumPoolSize | int | 最大線程池大小 |
3 | keepAliveTime | long | 線程最大空閑時(shí)間 |
4 | unit | TimeUnit | 時(shí)間單位 |
5 | workQueue | BlockingQueue<Runnable> | 線程等待隊(duì)列 |
6 | threadFactory | ThreadFactory | 線程創(chuàng)建工廠 |
7 | handler | RejectedExecutionHandler | 拒絕策略 |
我們先從系統(tǒng)提供的方法來深入了解下每個(gè)參數(shù)的含義
1.newFixedThreadPool(20)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
這個(gè)方法有1 個(gè)參數(shù),int threads 表示線程池中的數(shù)量,這個(gè)參數(shù)會(huì)直接給ThreadPoolExecutor的第一和第二個(gè)參數(shù),也就是corePoolSize和maximumPoolSize,剩下的參數(shù)中空余時(shí)間給的是 0,創(chuàng)建了一個(gè)新建了阻塞隊(duì)列LinkedBlockingQueue,返回值是ExecutorService。
2.newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
這個(gè)方法沒有參數(shù),和newFixedThreadPool區(qū)別是corePoolSize和maximumPoolSize兩個(gè)參數(shù)給了都是 1,需要注意這里最終返回的是FinalizableDelegatedExecutorService
。有個(gè)問題想一想,一個(gè)線程也要用線程池么?意義在哪里?
3.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
這個(gè)看起來參數(shù)都挺全的,首先corePoolSize是 0,maximumPoolSize給了 int 最大值,keepAliveTime是 60 ,時(shí)間單位是秒,這里注意等待隊(duì)列和前面兩個(gè)不一樣這里是SynchronousQueue。返回值是ExecutorService。
4.newScheduledThreadPool(20)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
這個(gè)高級點(diǎn)了,自己是個(gè)對象當(dāng)然是集成ThreadPoolExecutor類,我們看下他的構(gòu)造方法有兩個(gè),這里直接看一個(gè)參數(shù)的構(gòu)造方法。返回值是ScheduledThreadPoolExecutor,這里注意和前面三個(gè)都不一樣。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
同樣這里入?yún)⒑颓逦耍瑓?shù)名是corePoolSize核心線程數(shù)量直接給了我們ThreadPoolExecutor的第一個(gè)參數(shù)corePoolSize,第二個(gè)maximumPoolSize最大線程池大小和newCachedThreadPool一樣給的是 int 最大值,第三個(gè)看起來是個(gè)常量我們看下代碼中是 DEFAULT_KEEPALIVE_MILLIS = 10L
,單位是秒,這里的等待隊(duì)列和前面的也不一樣是DelayedWorkQueue優(yōu)先級隊(duì)列。
到此系統(tǒng)提供的四種創(chuàng)建線程池的參數(shù)都一一看了一遍,接下來我們看下為什么系統(tǒng)要提供這四種方法給我們,根據(jù)構(gòu)造方法中的參數(shù),有什么不一樣的使用場景,可以先想一想不著急忘記下。
現(xiàn)在就開始分析下四個(gè)方法創(chuàng)建出的線程池的使用場景。
1. newFixedThreadPool(白話文:固定數(shù)目的線程池)
通過上面的構(gòu)造方法我們知道,newFixedThreadPool核心線程池等于最大線程池,當(dāng)前的線程數(shù)能夠比較穩(wěn)定保證一個(gè)數(shù)。能夠避免頻繁回收線程和創(chuàng)建線程。故適用于處理cpu密集型的任務(wù),確保cpu在長期被工作線程使用的情況下,盡可能少的分配線程,即適用長期的任務(wù)。
缺點(diǎn):
達(dá)到線程池最大容量后,如果有任務(wù)完成讓出占用線程,那么此線程就會(huì)一直處于等待狀態(tài),而不會(huì)消亡,直到下一個(gè)任務(wù)再次占用該線程。這就可能會(huì)使用無界隊(duì)列來存放排隊(duì)任務(wù),當(dāng)大量任務(wù)超過線程池最大容量需要處理時(shí),隊(duì)列無線增大,會(huì)占用大量資源。
2. newSingleThreadExecutor(白話文:唯一線程線程池)
由于是唯一的嘛,最適用串行化任務(wù)。
3. newCachedThreadPool(白話文:可緩存線程的線程池)
newCacehedThreadPool 的最大特點(diǎn)就是,線程數(shù)量不固定。只要有空閑線程空閑時(shí)間超過keepAliveTime,就會(huì)被回收。有新的任務(wù),查看是否有線程處于空閑狀態(tài),如果不是就直接創(chuàng)建新的任務(wù)。故適用用于并發(fā)不固定的短期小任務(wù)。
缺點(diǎn):
線程池沒有最大線程數(shù)量限制,如果大量的任務(wù)同時(shí)提交,可能導(dǎo)致創(chuàng)線程過多會(huì)而導(dǎo)致資源耗盡。
4. newScheduledThreadPool(白話文:定時(shí)及周期執(zhí)行的線程池)
適用于定時(shí)操作一些任務(wù)