Java通過Executors提供四種線程池闷供,分別為:
newCachedThreadPool創(chuàng)建一個可緩存線程池模聋,如果線程池的大小超過了處理任務(wù)所需要的線程晨雳,那么就會回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時叹阔,此線程池又可以智能的添加新線程來處理任務(wù)质蕉。此線程池不會對線程池大小做限制呢撞,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
newFixedThreadPool 創(chuàng)建一個定長線程池饰剥,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待摧阅。
newScheduledThreadPool 創(chuàng)建一個無限長線程池汰蓉,支持定時及周期性任務(wù)執(zhí)行。
newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池棒卷,它只會用唯一的工作線程來執(zhí)行任務(wù)顾孽,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
(1). newCachedThreadPool
創(chuàng)建一個可緩存線程池比规,如果線程池長度超過處理需要若厚,可靈活回收空閑線程,若無可回收蜒什,則新建線程测秸。示例代碼如下:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(index);
}
});
}
線程池為無限大,當(dāng)執(zhí)行第二個任務(wù)時第一個任務(wù)已經(jīng)完成灾常,會復(fù)用執(zhí)行第一個任務(wù)的線程霎冯,而不用每次新建線程。
(2). newFixedThreadPool
創(chuàng)建一個定長線程池钞瀑,可控制線程最大并發(fā)數(shù)沈撞,超出的線程會在隊列中等待。示例代碼如下:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
因為線程池大小為3雕什,每個任務(wù)輸出index后sleep 2秒缠俺,所以每兩秒打印3個數(shù)字。
定長線程池的大小最好根據(jù)系統(tǒng)資源進(jìn)行設(shè)置贷岸。如Runtime.getRuntime().availableProcessors()壹士。可參考PreloadDataCache偿警。
(3) newScheduledThreadPool
創(chuàng)建一個大小無限的線程池墓卦,支持定時及周期性任務(wù)執(zhí)行。延遲執(zhí)行示例代碼如下:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
表示延遲3秒執(zhí)行户敬。
scheduleAtFixedRate:在任務(wù)執(zhí)行時間小于間隔時間的情況下落剪,程序以起始時間為準(zhǔn)則睁本,每隔指定時間執(zhí)行一次,不受任務(wù)執(zhí)行時間影響忠怖;當(dāng)執(zhí)行任務(wù)時間大于間隔時間呢堰,等待原有任務(wù)執(zhí)行完成,馬上開啟下一個任務(wù)進(jìn)行執(zhí)行凡泣。此時枉疼,執(zhí)行間隔時間已經(jīng)被打亂。
scheduleWithFixedDelay:無論任務(wù)執(zhí)行時間長短鞋拟,都是當(dāng)?shù)谝粋€任務(wù)執(zhí)行完成之后骂维,延遲指定時間再開始執(zhí)行第二個任務(wù)。
定期執(zhí)行示例代碼如下:
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
表示延遲1秒后每3秒執(zhí)行一次贺纲。
ScheduledExecutorService比Timer更安全航闺,功能更強(qiáng)大,后面會有一篇單獨進(jìn)行對比猴誊。
(4)潦刃、newSingleThreadExecutor
創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù)懈叹,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行乖杠。示例代碼如下:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
結(jié)果依次輸出,相當(dāng)于順序執(zhí)行各個任務(wù)澄成。
現(xiàn)行大多數(shù)GUI程序都是單線程的胧洒。Android中單線程可用于數(shù)據(jù)庫操作,文件操作墨状,應(yīng)用批量安裝略荡,應(yīng)用批量刪除等不適合并發(fā)但可能IO阻塞性及影響UI線程響應(yīng)的操作。
線程池的作用:
線程池作用就是限制系統(tǒng)中執(zhí)行線程的數(shù)量歉胶。根據(jù)系統(tǒng)的環(huán)境情況汛兜,可以自動或手動設(shè)置線程數(shù)量,達(dá)到運(yùn)行的最佳效果通今;少了浪費了系統(tǒng)資源粥谬,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量辫塌,其他線程排 隊等候漏策。一個任務(wù)執(zhí)行完畢,再從隊列的中取最前面的任務(wù)開始執(zhí)行臼氨。若隊列中沒有等待進(jìn)程掺喻,線程池的這一資源處于等待。當(dāng)一個新任務(wù)需要運(yùn)行時,如果線程池 中有等待的工作線程感耙,就可以開始運(yùn)行了褂乍;否則進(jìn)入等待隊列。
為什么要用線程池:
1.減少了創(chuàng)建和銷毀線程的次數(shù)即硼,每個工作線程都可以被重復(fù)利用逃片,可執(zhí)行多個任務(wù)。
2.可以根據(jù)系統(tǒng)的承受能力只酥,調(diào)整線程池中工作線線程的數(shù)目褥实,防止因為消耗過多的內(nèi)存,而把服務(wù)器累趴下(每個線程需要大約1MB內(nèi)存裂允,線程開的越多损离,消耗的內(nèi)存也就越大,最后死機(jī))绝编。
3.Java里面線程池的頂級接口是Executor僻澎,但是嚴(yán)格意義上講Executor并不是一個線程池,而只是一個執(zhí)行線程的工具瓮增。真正的線程池接口是ExecutorService。
比較重要的幾個類:
- ExecutorService
真正的線程池接口哩俭。
- ScheduledExecutorService
能和Timer/TimerTask類似绷跑,解決那些需要任務(wù)重復(fù)執(zhí)行的問題。
- ThreadPoolExecutor
ExecutorService的默認(rèn)實現(xiàn)凡资。
- ScheduledThreadPoolExecutor
繼承ThreadPoolExecutor的ScheduledExecutorService接口實現(xiàn)砸捏,周期性任務(wù)調(diào)度的類實現(xiàn)。
ThreadPoolExecutor:
-
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler)
corePoolSize:線程池維護(hù)線程的最少數(shù)量 (core : 核心)
maximumPoolSize:線程池維護(hù)線程的最大數(shù)量
keepAliveTime: 線程池維護(hù)線程所允許的空閑時間
unit: 線程池維護(hù)線程所允許的空閑時間的單位
workQueue: 線程池所使用的緩沖隊列
handler: 線程池對拒絕任務(wù)的處理策略
一個任務(wù)通過execute(Runnable)方法被添加到線程池隙赁,任務(wù)就是一個Runnable類型的對象垦藏,任務(wù)的執(zhí)行方法就是Runnable類型對象的run()方法。
當(dāng)一個任務(wù)通過execute(Runnable)方法欲添加到線程池時:
如果線程池中運(yùn)行的線程 小于corePoolSize 伞访,即使線程池中的線程都處于空閑狀態(tài)掂骏,也要 創(chuàng)建新的線程 來處理被添加的任務(wù)。
如果線程池中運(yùn)行的線程大于等于corePoolSize厚掷,但是緩沖隊列workQueue未滿 弟灼,那么任務(wù)被放入緩沖隊列 。
如果此時線程池中的數(shù)量大于corePoolSize冒黑,緩沖隊列workQueue滿(即無法將請求加入隊列 )田绑,并且線程池中的數(shù)量小于maximumPoolSize,建新的線程 來處理被添加的任務(wù)抡爹。
如果此時線程池中的數(shù)量大于corePoolSize掩驱,緩沖隊列workQueue滿,并且線程池中的數(shù)量等于maximumPoolSize ,那么通過 handler 所指定的策略來處理此任務(wù)欧穴。
當(dāng)線程池中的線程數(shù)量大于 corePoolSize時民逼,如果某線程空閑時間超過keepAliveTime,線程將被終止 苔可。這樣缴挖,線程池可以動態(tài)的調(diào)整池中的線程數(shù)。
也就是:處理任務(wù)的優(yōu)先級為:
corePoolSize焚辅、任務(wù)隊列workQueue映屋、最大線程maximumPoolSize,如果三者都滿了同蜻,使用handler處理被拒絕的任務(wù)棚点。
-
一般情況下,隊列的大小遵循下面的公式:
queSize <= ClientTimeOut(秒) * TPS湾蔓; 隊列大小 小于等于 客戶端超時 * 每秒處理的交易數(shù)
unit可選的參數(shù)為java.util.concurrent.TimeUnit中的幾個靜態(tài)屬性:
NANOSECONDS瘫析、MICROSECONDS、MILLISECONDS默责、SECONDS贬循。
workQueue一共有三種
常用的是: java.util.concurrent.ArrayBlockingQueue
直接提交。 工作隊列的默認(rèn)選項是 SynchronousQueue 桃序,它將任務(wù)直接提交給線程而不保持它們 杖虾。在此,如果不存在可用于立即運(yùn)行任務(wù)的線程 媒熊,則試圖把任務(wù)加入隊列將失敗奇适,因此會構(gòu)造一個新的線程 。此策略可以避免在處理可能具有內(nèi)部依賴性的請求集時出現(xiàn)鎖芦鳍。直接提交通常要求無界 maximumPoolSizes 以避免拒絕新提交的任務(wù) 嚷往。當(dāng)命令以超過隊列所能處理的平均數(shù)連續(xù)到達(dá)時,此策略允許無界線程具有增長的可能性柠衅。
無界隊列皮仁。 使用無界隊列(例如,不具有預(yù)定義容量的 LinkedBlockingQueue )將導(dǎo)致在所有 corePoolSize 線程都忙時新任務(wù)在隊列中等待菲宴。這樣魂贬,創(chuàng)建的線程就不會超過 corePoolSize 。(因此裙顽,maximumPoolSize 的值也就無效了付燥。)當(dāng)每個任務(wù)完全獨立于其他任務(wù),即任務(wù)執(zhí)行互不影響時愈犹,適合于使用無界隊列键科;例如闻丑,在 Web 頁服務(wù)器中。這種排隊可用于處理瞬態(tài)突發(fā)請求勋颖,當(dāng)命令以超過隊列所能處理的平均數(shù)連續(xù)到達(dá)時嗦嗡,此策略允許無界線程具有增長的可能性。
有界隊列饭玲。 當(dāng)使用有限的 maximumPoolSizes 時侥祭,有界隊列(如 ArrayBlockingQueue )有助于防止資源耗盡 ,但是可能較難調(diào)整和控制茄厘。隊列大小和最大池大小可能需要相互折衷:使用大型隊列和小型池可以最大限度地降低 CPU 使用率矮冬、操作系統(tǒng)資源和上下文切換開銷,但是可能導(dǎo)致人工降低吞吐量次哈。如果任務(wù)頻繁阻塞(例如胎署,如果它們是 I/O 邊界),則系統(tǒng)可能為超過您許可的更多線程安排時間窑滞。使用小型隊列通常要求較大的池大小琼牧,CPU 使用率較高,但是可能遇到不可接受的調(diào)度開銷哀卫,這樣也會降低吞吐量巨坊。
使用無界queue可能會耗盡系統(tǒng)資源
使用有界queue可能不能很好的滿足性能,需要調(diào)節(jié)線程數(shù)和queue大小
線程數(shù)自然也有開銷此改,所以需要根據(jù)不同應(yīng)用進(jìn)行調(diào)節(jié)
handler有四個選擇
ThreadPoolExecutor.AbortPolicy()
//拋出java.util.concurrent.RejectedExecutionException異常ThreadPoolExecutor.CallerRunsPolicy()
//重試添加當(dāng)前的任務(wù)趾撵,他會自動重復(fù)調(diào)用execute()方法ThreadPoolExecutor.DiscardOldestPolicy()
//拋棄舊的任務(wù)ThreadPoolExecutor.DiscardPolicy()
// 拋棄當(dāng)前的任務(wù)