深入解析java線程池的實現(xiàn)機制(一)

在前面的文章中正压,我們使用線程的時候就去創(chuàng)建一個線程猜年,這樣實現(xiàn)起來非常簡便,但是就會有一個問題:

如果并發(fā)的線程數(shù)量很多怜珍,并且每個線程都是執(zhí)行一個時間很短的任務就結束了,這樣頻繁創(chuàng)建線程就會大大降低系統(tǒng)的效率凤粗,因為頻繁創(chuàng)建線程和銷毀線程需要時間和系統(tǒng)資源酥泛。

那么有沒有一種辦法使得線程可以復用今豆,就是執(zhí)行完一個任務,并不被銷毀柔袁,而是可以繼續(xù)執(zhí)行其他的任務呆躲?在Java中可以通過線程池來達到這樣的效果。

Exectors

Exectors工廠類提供了線程池的初始化接口捶索,主要有如下幾種(這幾種線程池的使用實例在之前文章都有案例)

newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

newFixedThreadPool初始化一個指定線程數(shù)的線程池插掂,線程池里一直存在固定個數(shù)的線程以供調用,其中使用LinkedBlockingQuene作為裝載任務線程的阻塞隊列腥例,不過當線程池沒有可執(zhí)行任務時辅甥,也不會釋放線程。

newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

newCachedThreadPool初始化一個可以緩存線程的線程池燎竖,默認緩存60s璃弄,線程池的線程數(shù)可達到Integer.MAX_VALUE,即2147483647构回,內部使用SynchronousQueue作為阻塞隊列,newCachedThreadPool在沒有任務執(zhí)行時夏块,當線程的空閑時間超過keepAliveTime,會自動釋放線程纤掸,當提交新任務時脐供,如果沒有空閑線程,則創(chuàng)建新線程執(zhí)行任務茁肠,會導致一定的系統(tǒng)開銷患民;所以,使用該線程池時垦梆,一定要注意控制并發(fā)的任務數(shù)匹颤,否則創(chuàng)建大量的線程可能導致該線程池線程數(shù)無限增長。

newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

newSingleThreadExecutor初始化的線程池中只有一個線程托猩,如果該線程異常結束印蓖,會重新創(chuàng)建一個新的線程繼續(xù)執(zhí)行任務,唯一的線程可以保證所提交任務的順序執(zhí)行京腥,內部使用LinkedBlockingQueue作為阻塞隊列赦肃。

newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

初始化的線程池可以在指定的時間內周期性的執(zhí)行所提交的任務,在實際的業(yè)務場景中可以使用該線程池定期的同步數(shù)據(jù)公浪,內部使用DelayedWorkQueue作為阻塞隊列他宛。

上述不同類型的線程池我們通過各自的源碼可以看出,除了newScheduledThreadPool的內部實現(xiàn)特殊一點之外欠气,其它幾個線程池都是基于ThreadPoolExecutor類實現(xiàn)的厅各。

java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類

因此如果要透徹地了解Java中的線程池,必須先了解這個類预柒。下面我們來看一下ThreadPoolExecutor類的具體的實現(xiàn)队塘。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

下面解釋下一下構造器中各個參數(shù)的含義:

corePoolSize核心池的大小袁梗,這個參數(shù)跟后面講述的線程池的實現(xiàn)原理有非常大的關系。在創(chuàng)建了線程池后憔古,默認情況下遮怜,線程池中并沒有任何線程,而是等待有任務到來才創(chuàng)建線程去執(zhí)行任務鸿市,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法锯梁,從這2個方法的名字就可以看出,是預創(chuàng)建線程的意思焰情,即在沒有任務到來之前就創(chuàng)建corePoolSize個線程或者一個線程涝桅。默認情況下,在創(chuàng)建了線程池后烙样,線程池中的線程數(shù)為0,當有任務來之后蕊肥,就會創(chuàng)建一個線程去執(zhí)行任務谒获,當線程池中的線程數(shù)目達到corePoolSize后,就會把到達的任務放到緩存隊列當中壁却;
maximumPoolSize線程池最大線程數(shù)批狱,這個參數(shù)也是一個非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個線程展东;
keepAliveTime表示線程沒有任務執(zhí)行時最多保持多久時間會終止赔硫。默認情況下,只有當線程池中的線程數(shù)大于corePoolSize時盐肃,keepAliveTime才會起作用爪膊,直到線程池中的線程數(shù)不大于corePoolSize,即當線程池中的線程數(shù)大于corePoolSize時砸王,如果一個線程空閑的時間達到keepAliveTime推盛,則會終止,直到線程池中的線程數(shù)不超過corePoolSize谦铃。但是如果調用了allowCoreThreadTimeOut(boolean)方法耘成,在線程池中的線程數(shù)不大于corePoolSize時,keepAliveTime參數(shù)也會起作用驹闰,直到線程池中的線程數(shù)為0瘪菌;
unit:參數(shù)keepAliveTime的時間單位,有7種取值嘹朗,在TimeUnit類中有7種靜態(tài)屬性:TimeUnit.DAYS; //天 TimeUnit.HOURS; //小時 TimeUnit.MINUTES; //分鐘 TimeUnit.SECONDS; //秒 TimeUnit.MILLISECONDS; //毫秒 TimeUnit.MICROSECONDS; //微妙 TimeUnit.NANOSECONDS; //納秒
workQueue:一個阻塞隊列师妙,用來存儲等待執(zhí)行的任務,這個參數(shù)的選擇也很重要骡显,會對線程池的運行過程產(chǎn)生重大影響.
threadFactory:創(chuàng)建線程的工廠疆栏,通過自定義的線程工廠可以給每個新建的線程設置一個具有識別度的線程名.
handler:表示當拒絕處理任務時的策略(我們在下一篇詳細分析相關的策略)

ThreadPoolExecutor主要方法
  • execute()方法:實際上是Executor中聲明的方法曾掂,在ThreadPoolExecutor進行了具體的實現(xiàn),這個方法是ThreadPoolExecutor的核心方法壁顶,通過這個方法可以向線程池提交一個任務珠洗,交由線程池去執(zhí)行。

  • submit()方法:是在ExecutorService中聲明的方法若专,在AbstractExecutorService就已經(jīng)有了具體的實現(xiàn)许蓖,在ThreadPoolExecutor中并沒有對其進行重寫,這個方法也是用來向線程池提交任務的调衰,但是它和execute()方法不同膊爪,它能夠返回任務執(zhí)行的結果,去看submit()方法的實現(xiàn)嚎莉,會發(fā)現(xiàn)它實際上還是調用的execute()方法米酬,只不過它利用了Future來獲取任務執(zhí)行結果。

  • shutdown():當線程池調用該方法時,線程池的狀態(tài)則立刻變成SHUTDOWN狀態(tài),以后不能再往線程池中添加任何任務趋箩,否則將會拋出RejectedExecutionException異常赃额。但是,此時線程池不會立刻退出叫确,直到添加到線程池中的任務都已經(jīng)處理完成跳芳,才會退出。

  • shutdownNow():它通過調用Thread.interrupt來實現(xiàn)線程的立即退出竹勉,不會等待添加到線程池中的任務都已經(jīng)處理完成飞盆。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市次乓,隨后出現(xiàn)的幾起案子吓歇,更是在濱河造成了極大的恐慌,老刑警劉巖檬输,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件照瘾,死亡現(xiàn)場離奇詭異,居然都是意外死亡丧慈,警方通過查閱死者的電腦和手機析命,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逃默,“玉大人鹃愤,你說我怎么就攤上這事⊥暧颍” “怎么了软吐?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吟税。 經(jīng)常有香客問我凹耙,道長姿现,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任肖抱,我火速辦了婚禮备典,結果婚禮上,老公的妹妹穿的比我還像新娘意述。我一直安慰自己提佣,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布荤崇。 她就那樣靜靜地躺著拌屏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪术荤。 梳的紋絲不亂的頭發(fā)上倚喂,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音瓣戚,去河邊找鬼务唐。 笑死,一個胖子當著我的面吹牛带兜,可吹牛的內容都是我干的。 我是一名探鬼主播吨灭,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刚照,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了喧兄?” 一聲冷哼從身側響起无畔,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吠冤,沒想到半個月后浑彰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡拯辙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年郭变,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涯保。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡诉濒,死狀恐怖,靈堂內的尸體忽然破棺而出夕春,到底是詐尸還是另有隱情未荒,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布及志,位于F島的核電站片排,受9級特大地震影響寨腔,放射性物質發(fā)生泄漏。R本人自食惡果不足惜率寡,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一迫卢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勇劣,春花似錦靖避、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至命咐,卻和暖如春篡九,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背醋奠。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工榛臼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窜司。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓沛善,卻偏偏與公主長得像,于是被迫代替她去往敵國和親塞祈。 傳聞我的和親對象是個殘疾皇子金刁,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容