java線程池的使用

Java通過Executors提供四種線程池闷供,分別為:

  1. newCachedThreadPool創(chuàng)建一個可緩存線程池模聋,如果線程池的大小超過了處理任務(wù)所需要的線程晨雳,那么就會回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時叹阔,此線程池又可以智能的添加新線程來處理任務(wù)质蕉。此線程池不會對線程池大小做限制呢撞,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。

  2. newFixedThreadPool 創(chuàng)建一個定長線程池饰剥,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待摧阅。

  3. newScheduledThreadPool 創(chuàng)建一個無限長線程池汰蓉,支持定時及周期性任務(wù)執(zhí)行。

  4. 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)方法欲添加到線程池時:

  1. 如果線程池中運(yùn)行的線程 小于corePoolSize 伞访,即使線程池中的線程都處于空閑狀態(tài)掂骏,也要 創(chuàng)建新的線程 來處理被添加的任務(wù)。

  2. 如果線程池中運(yùn)行的線程大于等于corePoolSize厚掷,但是緩沖隊列workQueue未滿 弟灼,那么任務(wù)被放入緩沖隊列 。

  3. 如果此時線程池中的數(shù)量大于corePoolSize冒黑,緩沖隊列workQueue滿(即無法將請求加入隊列 )田绑,并且線程池中的數(shù)量小于maximumPoolSize,建新的線程 來處理被添加的任務(wù)抡爹。

  4. 如果此時線程池中的數(shù)量大于corePoolSize掩驱,緩沖隊列workQueue滿,并且線程池中的數(shù)量等于maximumPoolSize ,那么通過 handler 所指定的策略來處理此任務(wù)欧穴。

  5. 當(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有四個選擇

  1. ThreadPoolExecutor.AbortPolicy()
    //拋出java.util.concurrent.RejectedExecutionException異常

  2. ThreadPoolExecutor.CallerRunsPolicy()
    //重試添加當(dāng)前的任務(wù)趾撵,他會自動重復(fù)調(diào)用execute()方法

  3. ThreadPoolExecutor.DiscardOldestPolicy()
    //拋棄舊的任務(wù)

  4. ThreadPoolExecutor.DiscardPolicy()
    // 拋棄當(dāng)前的任務(wù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市带斑,隨后出現(xiàn)的幾起案子鼓寺,更是在濱河造成了極大的恐慌勋拟,老刑警劉巖勋磕,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異敢靡,居然都是意外死亡挂滓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門啸胧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赶站,“玉大人,你說我怎么就攤上這事纺念”创唬” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵陷谱,是天一觀的道長烙博。 經(jīng)常有香客問我瑟蜈,道長,這世上最難降的妖魔是什么渣窜? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任铺根,我火速辦了婚禮,結(jié)果婚禮上乔宿,老公的妹妹穿的比我還像新娘位迂。我一直安慰自己,他們只是感情好详瑞,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布掂林。 她就那樣靜靜地躺著,像睡著了一般蛤虐。 火紅的嫁衣襯著肌膚如雪党饮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天驳庭,我揣著相機(jī)與錄音刑顺,去河邊找鬼。 笑死饲常,一個胖子當(dāng)著我的面吹牛蹲堂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贝淤,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼柒竞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了播聪?” 一聲冷哼從身側(cè)響起朽基,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎离陶,沒想到半個月后稼虎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡招刨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年躲查,在試婚紗的時候發(fā)現(xiàn)自己被綠了猜旬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媳纬。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡填渠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谎倔,到底是詐尸還是另有隱情柳击,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布片习,位于F島的核電站捌肴,受9級特大地震影響彤守,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哭靖,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一具垫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧试幽,春花似錦筝蚕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至济榨,卻和暖如春坯沪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擒滑。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工腐晾, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丐一。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓藻糖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親库车。 傳聞我的和親對象是個殘疾皇子巨柒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容