ThreadPoolExecutor線程池

線程池作用是限制系統(tǒng)中執(zhí)行線程的數(shù)量总滩。根據(jù)系統(tǒng)的環(huán)境情況,可以自動(dòng)或手動(dòng)設(shè)置線程數(shù)量幸逆,達(dá)到運(yùn)行的最佳效果棍辕;少了浪費(fèi)系統(tǒng)資源暮现,多了造成系統(tǒng)擁擠效率不高。用線程池控制線程數(shù)量痢毒,其他線程排隊(duì)等候送矩。一個(gè)任務(wù)執(zhí)行完畢,再?gòu)年?duì)列中取出最前面的任務(wù)開始執(zhí)行哪替。

1. ThreadPoolExecutor

線程池的繼承關(guān)系

實(shí)際上,一般說的線程池接口菇怀,基本上說的就是ExecutorService凭舶。ExecutorService源碼中有各種API幫助我們使用,ExecutorService接口的默認(rèn)實(shí)現(xiàn)類為ThreadPoolExecutor爱沟。

1.1 線程池的構(gòu)造函數(shù)

線程池的構(gòu)造函數(shù)

構(gòu)造參數(shù)的含義:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

1. corePoolSize:核心線程數(shù)量帅霜。
默認(rèn)情況下,在創(chuàng)建了線程池后呼伸,線程池中的線程數(shù)為0身冀,當(dāng)有任務(wù)來之后,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù)括享,當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后搂根,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;
核心線程默認(rèn)會(huì)一直存活在線程池中铃辖,即使核心線程處于閑置狀態(tài)剩愧。
如果指定ThreadPoolExecutor的allowCoreThreadTimeout=true,那么核心線程若處于閑置狀態(tài)的話娇斩,超過一定的時(shí)間(KeepAliveTime)仁卷,就會(huì)銷毀掉。

2. maximumPoolSize:線程池最大線程數(shù)
線程池中最多能創(chuàng)建多少個(gè)線程犬第。

3. keepAliveTime:非核心線程閑置超時(shí)時(shí)間
默認(rèn)情況下锦积,當(dāng)線程池的線程數(shù)大于corePoolSize時(shí),keepAliveTime才會(huì)起作用歉嗓,直到線程池的線程數(shù)不大于corePoolSize丰介。

4. TimeUnit:keepAliveTime:時(shí)間單位
MILLISECONDS : 1毫秒 、SECONDS : 秒遥椿、MINUTES : 分基矮、HOURS : 小時(shí)、DAYS : 天

5. BlockingQueue<Runnable>:阻塞隊(duì)列
一個(gè)阻塞隊(duì)列冠场,用于存儲(chǔ)等待執(zhí)行的任務(wù)家浇。一般使用LinkedBlockingQueue或者Synchronous(誰坤尼斯)。維護(hù)著等待執(zhí)行的Runnable對(duì)象碴裙。若corePoolSize滿了之后钢悲,則新添加的任務(wù)會(huì)被添加到這個(gè)隊(duì)列中等待處理点额,如果隊(duì)列滿了,則創(chuàng)建非核心線程執(zhí)行任務(wù)莺琳。

6. ThreadFactory:創(chuàng)建線程的方式
這是一個(gè)接口还棱,Thread newThread(Runnable r),需要實(shí)現(xiàn)該方法惭等。

7. RejectedExecutionHandler:拒絕策略
當(dāng)線程無法執(zhí)行新任務(wù)(一般是由于線程池的線程數(shù)量已經(jīng)達(dá)到最大數(shù)(maximumPoolSize)或者線程池關(guān)閉導(dǎo)致的)默認(rèn)情況下當(dāng)線程池?zé)o法處理新線程珍手,會(huì)拋出RejectedExecutionException。

1.2 線程池的生命周期

線程池的生命周期
  1. 當(dāng)創(chuàng)建線程池后辞做,初始時(shí)琳要,線程池處于Running狀態(tài)。
  2. 當(dāng)調(diào)用shutdown()方法秤茅,則線程池處于shutdown狀態(tài)稚补,此時(shí)線程池不能接受新的任務(wù),他會(huì)等待所有任務(wù)執(zhí)行完畢框喳。
  3. 當(dāng)調(diào)用shutdownNow()方法课幕,則線程處于stop狀態(tài),此時(shí)線程池不能接受新的任務(wù)五垮,并且試圖嘗試終止正在執(zhí)行的任務(wù)乍惊。
  4. 當(dāng)線程池處于shutdown狀態(tài)或者stop狀態(tài),并且所有工作線程已經(jīng)銷毀拼余,任務(wù)緩存隊(duì)列已經(jīng)清空或者執(zhí)行結(jié)束后污桦,線程池被設(shè)置為terminated(終止譚母你提的)。

1.3 線程池任務(wù)執(zhí)行流程

corePoolSize就是核心線程池的大小匙监,maxmumPoolSize就是線程池的任務(wù)量過大時(shí)的一種補(bǔ)救措施凡橱。

  1. 當(dāng)線程池中線程數(shù)量小于corePoolSize,每來一個(gè)任務(wù)亭姥,就會(huì)創(chuàng)建一個(gè)線程執(zhí)行這個(gè)任務(wù)稼钩。
  2. 當(dāng)前線程池線程數(shù)量大于等于corePoolSize,則每來一個(gè)任務(wù)达罗。會(huì)嘗試將其添加到任務(wù)緩存隊(duì)列中坝撑,若是添加成功,則該任務(wù)會(huì)等待線程將其取出去執(zhí)行粮揉;若添加失斞怖睢(一般來說任務(wù)緩存隊(duì)列已滿),則會(huì)嘗試創(chuàng)建新的線程執(zhí)行扶认。
  3. 當(dāng)前線程池線程數(shù)量等于maximumPoolSize侨拦,則會(huì)采取任務(wù)拒絕策略進(jìn)行處理。
  4. 當(dāng)前線程池線程數(shù)量大于corePoolSize辐宾,如果某線程空閑時(shí)間超過keepAliveTime狱从,線程將會(huì)被終止膨蛮,直到不大于corePoolSize數(shù)量季研。如果允許為核心池(corePoolSize)中的線程設(shè)置存活時(shí)間与涡,那么核心池中的線程空閑時(shí)間超過keepAliveTime豺鼻,線程也會(huì)被終止谬莹。

1.4 線程池中的線程初始化

默認(rèn)情況下附帽,創(chuàng)建線程池之后整胃,線程池是沒有線程的屁使,需要提交任務(wù)才會(huì)創(chuàng)建線程。

在實(shí)際中如果需要線程池創(chuàng)建之后酬蹋,立即創(chuàng)建線程,可以通過:

  • java.util.concurrent.ThreadPoolExecutor#prestartCoreThread:初始化一個(gè)核心線程。
  • java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads:初始化所有核心線程年缎。

1.5 任務(wù)緩存隊(duì)列及排隊(duì)策略

java.util.concurrent.BlockingQueue的特性是:當(dāng)隊(duì)列是空的時(shí)候,從隊(duì)列中獲取或刪除元素將被阻塞堂淡,或者當(dāng)隊(duì)列是滿時(shí),往隊(duì)列中添加元素會(huì)被阻塞。

BlockQueue實(shí)現(xiàn)類

阻塞隊(duì)列不接受空值,當(dāng)嘗試向隊(duì)列中添加空值時(shí),會(huì)拋出NullPointerException。
阻塞隊(duì)列都是線程安全的,所有的查詢方法都是原子的梗顺,并且使用了內(nèi)部鎖或者其他形式的并發(fā)控制寺谤。

  • ArrayBlockingQueue:基于數(shù)組的先進(jìn)先出隊(duì)列,此隊(duì)列創(chuàng)建必須指定大兴辈ァ变屁;
  • LinkedBlockingQueue:基于鏈表的先進(jìn)先出,默認(rèn)大小無限大(注意內(nèi)存溢出)意狠,吞吐量通常高于ArrayBlockingQueue粟关。
  • synchronousQueue:不存儲(chǔ)元素的存儲(chǔ)隊(duì)列,每個(gè)插入操作必須等待另一個(gè)線程調(diào)用移除操作环戈,否則該插入操作一直處于阻塞狀態(tài)闷板。吞吐量要高于LinkedBlockingQueue澎灸。

1.6 任務(wù)拒絕策略

線程池的任務(wù)緩存隊(duì)列已滿并且線程池中線程數(shù)量已達(dá)到maxmumPoolSize,如果還有任務(wù)的到來就會(huì)采取拒絕策略遮晚。通常有四種處理策略性昭。

1.6.1 線程池默認(rèn)的處理策略

處理策略 效果
AbortPolicy 丟棄任務(wù)并拋出RejectedExecutionException異常
DiscardPolicy 也是丟棄任務(wù),但是不拋出異常
DiscardOldestPolicy 丟棄隊(duì)列最前面的任務(wù)县遣,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
CallerRunsPolicy 只要線程池未關(guān)閉糜颠,該策略直接在調(diào)用者線程中串行運(yùn)行被丟棄的任務(wù),顯然這樣不會(huì)真的丟棄任務(wù)萧求,但是可能會(huì)造成調(diào)用者性能急劇下降

1.6.2 自定義線程池處理策略

@Slf4j
public class PrintingPolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        log.info(r.toString() + "被拋棄了其兴!");
    }
}

有一種場(chǎng)景是:當(dāng)線程池滿了之后,我們的拒絕策略:主線程保持阻塞夸政,線程池有資源了元旬,主線程在將任務(wù)交由線程池處理∈匚剩【不丟棄任務(wù)法绵,當(dāng)線程池沒資源,主線程等待線程池有資源】

因?yàn)槲覀円话懵暶骶€程池酪碘,使用的是ArrayBlockingQueue有界的阻塞隊(duì)列,而BlockingQueue特點(diǎn)是當(dāng)隊(duì)列滿了盐茎,線程向隊(duì)列存數(shù)據(jù)兴垦,線程會(huì)被阻塞。

@Slf4j
public class BlockPolicy implements RejectedExecutionHandler {
      @Override
       public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
           try {
                executor.getQueue().put(r);
           } catch (InterruptedException e) {
                 log.error("", e);
          }
      }
}

1.7 線程池的關(guān)閉

ThreadPoolExecutor提供了兩個(gè)方法字柠,用于線程池的關(guān)閉探越。
shutdown():不會(huì)立即終止線程池,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止窑业,但再也不會(huì)接受新的任務(wù)
shutdownNow():立即終止線程池钦幔,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊(duì)列常柄,返回尚未執(zhí)行的任務(wù)

1.8 線程池容量的動(dòng)態(tài)調(diào)整

ThreadPoolExcutor提供了動(dòng)態(tài)調(diào)整線程容量大小的方法:
setCorePoolSize:設(shè)置核心池大小鲤氢。
setMaximumPoolSize:設(shè)置線程池最大能創(chuàng)建的線程數(shù)目大小。

1.9 創(chuàng)建線程池的API

創(chuàng)建線程池的API

Executors類中也提供了幾個(gè)靜態(tài)方法來創(chuàng)建線程池:(不推薦使用)

1. java.util.concurrent.Executors#newCachedThreadPool()

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

corePoolSize大小為0西潘;maxmumPoolSize為無限大卷玉;任務(wù)隊(duì)列為SynchronousQueue,該隊(duì)列沒有緩存區(qū)喷市,即不會(huì)有任務(wù)在該隊(duì)列中排隊(duì)相种。

該線程池時(shí)為了緩存頻繁用到的線程。

因?yàn)槭?strong>"緩存"線程池品姓,沒有緩存可以永久有效寝并,因此coreSizePool為0箫措,因此任務(wù)隊(duì)列的緩存區(qū)應(yīng)該為0。否則即便系統(tǒng)有可用線程資源衬潦,當(dāng)有新的任務(wù)進(jìn)入時(shí)也不會(huì)立即執(zhí)行斤蔓,而是進(jìn)入任務(wù)隊(duì)列排隊(duì)直到隊(duì)列滿,這樣顯然不合理别渔。

由于隊(duì)列緩沖區(qū)為空附迷,那么每來一個(gè)任務(wù),都會(huì)新建線程執(zhí)行任務(wù)哎媚,這樣可能會(huì)導(dǎo)致大量線程被創(chuàng)建喇伯,進(jìn)而系統(tǒng)癱瘓。

2. java.util.concurrent.Executors#newSingleThreadExecutor()

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

單線程化的線程池拨与,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù)稻据。但是需要注意的是大量任務(wù)堆積在LinkedBlockingQueue中,可能會(huì)造成內(nèi)存溢出买喧。

3. java.util.concurrent.Executors#newFixedThreadPool(int)

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

創(chuàng)建定長(zhǎng)線程池捻悯,可控制線程最大并發(fā)數(shù),超過的線程會(huì)在隊(duì)列中等待淤毛。

注意事項(xiàng)

若是線程池采用的是LinkedBlockingQueue今缚,那么若是消費(fèi)的速度小于生產(chǎn)的速度,內(nèi)存隨著時(shí)間的堆積低淡,可能會(huì)造成內(nèi)存溢出姓言。

解決方案:可以采用有界隊(duì)列,可能造成部分任務(wù)被丟棄蔗蹋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末何荚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子猪杭,更是在濱河造成了極大的恐慌餐塘,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皂吮,死亡現(xiàn)場(chǎng)離奇詭異戒傻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)涮较,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門稠鼻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狂票,你說我怎么就攤上這事候齿。” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵慌盯,是天一觀的道長(zhǎng)周霉。 經(jīng)常有香客問我,道長(zhǎng)亚皂,這世上最難降的妖魔是什么俱箱? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮灭必,結(jié)果婚禮上狞谱,老公的妹妹穿的比我還像新娘。我一直安慰自己禁漓,他們只是感情好跟衅,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著播歼,像睡著了一般伶跷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秘狞,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天叭莫,我揣著相機(jī)與錄音,去河邊找鬼烁试。 笑死雇初,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的减响。 我是一名探鬼主播抵皱,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼辩蛋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起移盆,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤悼院,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后咒循,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體据途,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年叙甸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颖医。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出审轮,到底是詐尸還是另有隱情乙墙,我是刑警寧澤阵难,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布段只,位于F島的核電站惜浅,受9級(jí)特大地震影響问欠,放射性物質(zhì)發(fā)生泄漏俺榆。R本人自食惡果不足惜感昼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罐脊。 院中可真熱鬧定嗓,春花似錦、人聲如沸萍桌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梗夸。三九已至层玲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間反症,已是汗流浹背辛块。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铅碍,地道東北人润绵。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胞谈,于是被迫代替她去往敵國(guó)和親尘盼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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