ThreadPoolExecutor簡(jiǎn)析

為什么使用線程池

使用線程池的好處是減少在創(chuàng)建和銷(xiāo)毀線程上所花的時(shí)間和系統(tǒng)資源的開(kāi)銷(xiāo)沪曙,節(jié)省資源鹊奖,提高效率吠裆。如果不使用線程池渐逃,有可能會(huì)造成系統(tǒng)創(chuàng)建大量同類(lèi)線程而導(dǎo)致內(nèi)存耗盡或者“過(guò)度切換”的問(wèn)題够掠。所以線程資源最好由線程池提供,不要在應(yīng)用中自行顯示創(chuàng)建線程朴乖。

Executor

Executor是java中線程池的核心接口祖屏,只有一個(gè)方法execute(Runnable command)

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

類(lèi)關(guān)系說(shuō)明

ThreadPoolExecutor是Executor接口的主要實(shí)現(xiàn)類(lèi)

類(lèi)關(guān)系
類(lèi)關(guān)系

ExecutorService是Executor的子接口,增加了一些對(duì)線程池的控制方法买羞,關(guān)閉線程池袁勺,批量調(diào)用等
AbstractExecutorService是一個(gè)抽象類(lèi),對(duì)ExecutorService的方法提供了默認(rèn)實(shí)現(xiàn)畜普,ThreadPoolExecutor繼承此類(lèi)

ThreadPoolExecutor

構(gòu)造方法

ThreadPoolExecutor是線程池的真正實(shí)現(xiàn)類(lèi)期丰,可以通過(guò)構(gòu)造方法的各種參數(shù)組合來(lái)配置不同的線程池援奢。構(gòu)造方法有以下四種

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
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;
}

先來(lái)解釋一下各個(gè)參數(shù):

  • int corePoolSize

    該線程池中核心線程數(shù)掰派。線程池在新建線程的時(shí)候困介,如果當(dāng)前活動(dòng)線程總數(shù)小于核心線程數(shù)帆调,則會(huì)新建核心線程,直到達(dá)到corePoolSize癣漆,如果超過(guò)核心線程數(shù)骑祟,則會(huì)創(chuàng)建臨時(shí)線程晦攒,也就是非核心線程逛犹,兩者的區(qū)別在于非核心線程在執(zhí)行完任務(wù)后超過(guò)規(guī)定時(shí)長(zhǎng)會(huì)被銷(xiāo)毀端辱,而核心線程執(zhí)行完任務(wù)會(huì)一直存在,不論是否閑置虽画。

  • int maximumPoolSize

    該線程池中線程總數(shù)的最大值舞蔽。線程總數(shù)=核心線程數(shù)+非核心線程數(shù)

  • long keepAliveTime

    該線程池中非核心線程的存活時(shí)間。一個(gè)非核心線程码撰,如果處于閑置狀態(tài)渗柿,當(dāng)閑置時(shí)長(zhǎng)超過(guò)這個(gè)參數(shù)設(shè)置的時(shí)間,則會(huì)被銷(xiāo)毀脖岛,如果指定ThreadPoolExecutor的allowCoreThreadTimeOut這個(gè)屬性為true朵栖,那么這個(gè)參數(shù)會(huì)同樣作用于核心線程

  • TimeUnit unit

    keepAliveTime的單位。TimeUnit是一個(gè)枚舉類(lèi)型柴梆,其包括:

    NANOSECONDS : 微毫秒 1微毫秒 = 1微秒 / 1000</br>
    MICROSECONDS : 微秒 1微秒 = 1毫秒 / 1000</br>
    MILLISECONDS : 毫秒 1毫秒 = 1秒 /1000</br>
    SECONDS : 秒</br>
    MINUTES : 分</br>
    HOURS : 小時(shí)</br>
    DAYS : 天</br>

  • BlockingQueue<Runnable> workQueue

    該線程池中的任務(wù)隊(duì)列混槐。維護(hù)著所有等待執(zhí)行的Runnable對(duì)象,常用的workQueue有:

    1. SynchronousQueue:同步隊(duì)列,一個(gè)沒(méi)有容量大小的阻塞隊(duì)列轩性,將任務(wù)同步交付給工作線程,也就是說(shuō)當(dāng)這個(gè)隊(duì)列接收到任務(wù)的時(shí)候,會(huì)直接提交給線程進(jìn)行處理揣苏,如果核心線程都已經(jīng)在工作了悯嗓,則會(huì)創(chuàng)建非核心線程,直到線程數(shù)達(dá)到maximumPoolSize卸察,從而執(zhí)行拒絕策略
    2. LinkedBlockingQueue:構(gòu)造函數(shù)不傳容量大小會(huì)默認(rèn)為Integer.MAX_VALUE脯厨,一般都使用默認(rèn)值,所以當(dāng)這個(gè)隊(duì)列接收到任務(wù)時(shí)坑质,如果當(dāng)前線程數(shù)小于核心線程數(shù)合武,則會(huì)創(chuàng)建核心線程,否則會(huì)進(jìn)入隊(duì)列等待涡扼,又因?yàn)殛?duì)列默認(rèn)大小是Integer.MAX_VALUE稼跳,即所有超過(guò)核心線程數(shù)的任務(wù)量都將被添加到隊(duì)列中去,從而導(dǎo)致maximumPoolSize參數(shù)失效吃沪,因?yàn)檫@個(gè)隊(duì)列幾乎不可能滿(mǎn)
    3. ArrayBlockingQueue:必須要限定容量大小的隊(duì)列汤善,當(dāng)接收到任務(wù)時(shí),如果沒(méi)有達(dá)到核心線程數(shù)票彪,則新建核心線程處理任務(wù)红淡,如果核心線程已滿(mǎn),則進(jìn)入隊(duì)列等待降铸,如果隊(duì)列也滿(mǎn)了在旱,則會(huì)創(chuàng)建非核心線程處理任務(wù),如果總線程數(shù)達(dá)到了maximumPoolSize推掸,則會(huì)執(zhí)行拒絕策略
    4. PriorityBlockingQueue:無(wú)界的阻塞隊(duì)列桶蝎,類(lèi)似于LinkedBlockingQueue,但其所含對(duì)象的順序并不是FIFO终佛,而是根據(jù)對(duì)象的自然排序順序或者構(gòu)造函數(shù)的Comparator決定的順序
  • ThreadFactory threadFactory:創(chuàng)建線程的方式俊嗽。一般不用,默認(rèn)實(shí)現(xiàn)即可

  • RejectedExecutionHandler handler:拒絕策略铃彰。當(dāng)線程數(shù)達(dá)到最大值且隊(duì)列已滿(mǎn)時(shí)會(huì)執(zhí)行拒絕策略绍豁,ThreadPoolExecutor自己提供了四個(gè)拒絕策略:

    1. AbortPolicy:默認(rèn)策略,直接放棄任務(wù)并拋出RejectedExecutionException異常
    2. CallerRunsPolicy:用調(diào)用者的線程執(zhí)行任務(wù)
    3. DiscardOldestPolicy:拋棄隊(duì)列中最老的那個(gè)任務(wù)牙捉,也就是排隊(duì)時(shí)間最長(zhǎng)的那個(gè)任務(wù)
    4. DiscardPolicy:拋棄當(dāng)前將要加入隊(duì)列的任務(wù)

主要方法

  • execute:開(kāi)始執(zhí)行一個(gè)任務(wù)
  • shutdown:?jiǎn)?dòng)之前提交的任務(wù)會(huì)執(zhí)行完并有序關(guān)閉竹揍,不接受新任務(wù),如果已經(jīng)關(guān)閉邪铲,調(diào)用沒(méi)有任何效果
  • shutdownNow:試圖立即停止所有積極執(zhí)行的任務(wù)芬位,停止處理等待的任務(wù),并返回未執(zhí)行的任務(wù)列表
  • remove:取消在隊(duì)列中等待還未執(zhí)行的任務(wù)带到,已執(zhí)行的無(wú)法取消

當(dāng)然還有一系列的getXxx方法昧碉,方便對(duì)線程池的監(jiān)控

getXxx方法
getXxx方法

總結(jié)

引用《阿里巴巴Java開(kāi)發(fā)手冊(cè)》中的一段話來(lái)總結(jié)一下

【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則被饿,規(guī)避資源耗盡的風(fēng)險(xiǎn)四康。

說(shuō)明:Executors 返回的線程池對(duì)象的弊端如下:

1)FixedThreadPool 和 SingleThreadPool:

允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE,可能會(huì)堆積大量的請(qǐng)求狭握,從而導(dǎo)致 OOM闪金。

2)CachedThreadPool 和 ScheduledThreadPool:

允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線程论颅,從而導(dǎo)致 OOM哎垦。

一個(gè)線程池在應(yīng)用中如果不再被引用且沒(méi)有剩余的線程時(shí),這個(gè)線程池會(huì)被自動(dòng)的shutdown恃疯。因此如果你想在忘記執(zhí)行shutdown方法時(shí)也能正常的關(guān)閉線程池漏设,建議設(shè)置一個(gè)有限的keepAliveTime,同時(shí)也執(zhí)行下 allowCoreThreadTimeOut(true)

enjoy conding...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末澡谭,一起剝皮案震驚了整個(gè)濱河市愿题,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛙奖,老刑警劉巖潘酗,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異雁仲,居然都是意外死亡仔夺,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)攒砖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缸兔,“玉大人,你說(shuō)我怎么就攤上這事吹艇《杳郏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵受神,是天一觀的道長(zhǎng)抛猖。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鼻听,這世上最難降的妖魔是什么财著? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮撑碴,結(jié)果婚禮上撑教,老公的妹妹穿的比我還像新娘。我一直安慰自己醉拓,他們只是感情好伟姐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布收苏。 她就那樣靜靜地躺著,像睡著了一般愤兵。 火紅的嫁衣襯著肌膚如雪倒戏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天恐似,我揣著相機(jī)與錄音,去河邊找鬼傍念。 笑死矫夷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的憋槐。 我是一名探鬼主播双藕,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼阳仔!你這毒婦竟也來(lái)了忧陪?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤近范,失蹤者是張志新(化名)和其女友劉穎嘶摊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體评矩,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡叶堆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斥杜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虱颗。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蔗喂,靈堂內(nèi)的尸體忽然破棺而出忘渔,到底是詐尸還是另有隱情,我是刑警寧澤缰儿,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布畦粮,位于F島的核電站,受9級(jí)特大地震影響返弹,放射性物質(zhì)發(fā)生泄漏锈玉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一义起、第九天 我趴在偏房一處隱蔽的房頂上張望拉背。 院中可真熱鬧,春花似錦默终、人聲如沸椅棺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)两疚。三九已至床估,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诱渤,已是汗流浹背丐巫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勺美,地道東北人递胧。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赡茸,于是被迫代替她去往敵國(guó)和親缎脾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354