并發(fā) - Java中的線程池

線程池

Java中的線程池是運(yùn)用場(chǎng)景最多的并發(fā)框架晚顷,幾乎所有需要異步或并發(fā)執(zhí)行任務(wù)的程序都可以使用線程池。在開(kāi)發(fā)過(guò)程中按声,合理地使用線程池能夠帶來(lái)3個(gè)好處燃少。

  • 降低資源消耗
    通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的資源消耗。
  • 提高響應(yīng)速度
    當(dāng)任務(wù)到達(dá)時(shí)席里,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行叔磷。
  • 提高線程的可管理性
    線程是稀缺資源,如果無(wú)限制地創(chuàng)建奖磁,不僅會(huì)消耗系統(tǒng)資源世澜,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一分配署穗、調(diào)優(yōu)和監(jiān)控寥裂。但是,要做到合理利用線程池案疲,必須對(duì)其實(shí)現(xiàn)原理了如指掌封恰。

線程池的實(shí)現(xiàn)原理

  1. 線程池判斷核心線程池里的線程是否都在執(zhí)行任務(wù)。如果不是褐啡,則創(chuàng)建一個(gè)新的工作線程來(lái)執(zhí)行任務(wù)诺舔。如果核心線程池里的線程都在執(zhí)行任務(wù),則進(jìn)入下個(gè)流程备畦。
  2. 線程池判斷工作隊(duì)列是否已經(jīng)滿低飒。如果工作隊(duì)列沒(méi)有滿,則將新提交的任務(wù)存儲(chǔ)在這個(gè)工作隊(duì)列里懂盐。如果工作隊(duì)列滿了褥赊,則進(jìn)入下個(gè)流程。
  3. 線程池判斷線程池的線程是否都處于工作狀態(tài)莉恼。如果沒(méi)有拌喉,則創(chuàng)建一個(gè)新的工作線程來(lái)執(zhí)行任務(wù)。如果已經(jīng)滿了俐银,則交給飽和策略來(lái)處理這個(gè)任務(wù)尿背。

線程池的使用

創(chuàng)建線程池

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,milliseconds,runnableTaskQueue, handler);
  • corePoolSize(線程池的基本大小)
    當(dāng)提交一個(gè)任務(wù)到線程池時(shí)捶惜,線程池會(huì)創(chuàng)建一個(gè)線程執(zhí)行任務(wù)田藐,即使其他空閑的基本線程能夠執(zhí)行新任務(wù)也會(huì)創(chuàng)建線程,等到需要執(zhí)行的任務(wù)數(shù)大于線程池基本大小時(shí)就不再創(chuàng)建。如果調(diào)用了線程池的prestartAllCoreThreads()方法汽久,線程池會(huì)提前創(chuàng)建并啟動(dòng)所有基本線程茴晋。
  • maximumPoolSize(線程池最大數(shù)量)
    線程池允許創(chuàng)建的最大線程數(shù)。如果隊(duì)列滿了回窘,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù)诺擅,則線程池會(huì)再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是啡直,如果使用了無(wú)界的任務(wù)隊(duì)列這個(gè)參數(shù)就沒(méi)什么效果烁涌。
  • keepAliveTime(線程活動(dòng)保持時(shí)間)
    線程池的工作線程空閑后,保持存活的時(shí)間酒觅。所以撮执,如果任務(wù)很多,并且每個(gè)任務(wù)執(zhí)行的時(shí)間比較短舷丹,可以調(diào)大時(shí)間抒钱,提高線程的利用率。
  • milliseconds(線程活動(dòng)保持時(shí)間的單位)
  • runnableTaskQueue(任務(wù)隊(duì)列)
    用于保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列颜凯∧北遥可以選擇以下幾個(gè)阻塞隊(duì)列。

ArrayBlockingQueue

是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列症概,此隊(duì)列按FIFO(先進(jìn)先出)原則對(duì)元素進(jìn)行排序蕾额。

LinkedBlockingQueue

一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO排序元素彼城,吞吐量通常要高于ArrayBlockingQueue诅蝶。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列。

SynchronousQueue

一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列募壕。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作调炬,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue舱馅,靜態(tài)工廠方Executors.newCachedThreadPool使用了這個(gè)隊(duì)列缰泡。

PriorityBlockingQueue

一個(gè)具有優(yōu)先級(jí)的無(wú)限阻塞隊(duì)列。

數(shù)組隊(duì)列和鏈表隊(duì)列異同點(diǎn)
  1. 隊(duì)列大小有所不同习柠,ArrayBlockingQueue是有界的初始化必須指定大小匀谣,而LinkedBlockingQueue可以是有界的也可以是無(wú)界的(Integer.MAX_VALUE)照棋,對(duì)于后者而言资溃,當(dāng)添加速度大于移除速度時(shí),在無(wú)界的情況下烈炭,可能會(huì)造成內(nèi)存溢出等問(wèn)題溶锭。
  2. 數(shù)據(jù)存儲(chǔ)容器不同,ArrayBlockingQueue采用的是數(shù)組作為數(shù)據(jù)存儲(chǔ)容器符隙,而LinkedBlockingQueue采用的則是以Node節(jié)點(diǎn)作為連接對(duì)象的鏈表趴捅。
  3. 由于ArrayBlockingQueue采用的是數(shù)組的存儲(chǔ)容器垫毙,因此在插入或刪除元素時(shí)不會(huì)產(chǎn)生或銷毀任何額外的對(duì)象實(shí)例,而LinkedBlockingQueue則會(huì)生成一個(gè)額外的Node對(duì)象拱绑。這可能在長(zhǎng)時(shí)間內(nèi)需要高效并發(fā)地處理大批量數(shù)據(jù)的時(shí),對(duì)于GC可能存在較大影響猎拨。
  4. 兩者的實(shí)現(xiàn)隊(duì)列添加或移除的鎖不一樣膀藐,ArrayBlockingQueue實(shí)現(xiàn)的隊(duì)列中的鎖是沒(méi)有分離的额各,即添加操作和移除操作采用的同一個(gè)ReenterLock鎖,而LinkedBlockingQueue實(shí)現(xiàn)的隊(duì)列中的鎖是分離的吧恃,其添加采用的是putLock,移除采用的則是takeLock痕寓,這樣能大大提高隊(duì)列的吞吐量,也意味著在高并發(fā)的情況下生產(chǎn)者和消費(fèi)者可以并行地操作隊(duì)列中的數(shù)據(jù)呻率,以此來(lái)提高整個(gè)隊(duì)列的并發(fā)性能。

handler(RejectedExecutionHandler)(飽和策略)

當(dāng)隊(duì)列和線程池都滿了筷凤,說(shuō)明線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)藐守。這個(gè)策略默認(rèn)情況下是AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常卢厂。在JDK 1.5中Java線程池框架提供了以下4種策略。

  • AbortPolicy:直接拋出異常慎恒。
  • CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)任内。
  • DiscardOldestPolicy:丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)融柬。
  • DiscardPolicy:不處理死嗦,丟棄掉。

提交任務(wù)

  • execute()
    用于提交不需要返回值的任務(wù)粒氧,所以無(wú)法判斷任務(wù)是否被線程池執(zhí)行成功越除。
threadsPool.execute(new Runnable() {
    @Override
    public void run() {
        // TODO Auto-generated method stub
    }
});
  • submit()
    用于提交需要返回值的任務(wù)。線程池會(huì)返回一個(gè)future類型的對(duì)象,通過(guò)這個(gè)future對(duì)象可以判斷任務(wù)是否執(zhí)行成功摘盆,并且可以通過(guò)future的get()方法來(lái)獲取返回值翼雀,get()方法會(huì)阻塞當(dāng)前線程直到任務(wù)完成,而使用get(long timeout孩擂,TimeUnit unit)方法則會(huì)阻塞當(dāng)前線程一段時(shí)間后立即返回狼渊,這時(shí)候有可能任務(wù)沒(méi)有執(zhí)行完。
Future<Object> future = threadsPool.submit(harReturnValuetask);
        try {
           Object s = future.get();
        } catch (InterruptedException e) {
           // 處理中斷異常
        } catch (ExecutionException e) {
           // 處理無(wú)法執(zhí)行任務(wù)異常
        } finally {
          // 關(guān)閉線程池
          executor.shutdown();
                }

關(guān)閉線程池

可以通過(guò)調(diào)用線程池的shutdown或shutdownNow方法來(lái)關(guān)閉線程池类垦。它們存在一定的區(qū)別囤锉,shutdownNow首先將線程池的狀態(tài)設(shè)置成STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程护锤,并返回等待執(zhí)行任務(wù)的列表官地,而shutdown只是將線程池的狀態(tài)設(shè)置成SHUTDOWN狀態(tài),然后中斷所有沒(méi)有正在執(zhí)行任務(wù)的線程烙懦。

配置線程池

要想合理地配置線程池驱入,就必須首先分析任務(wù)特性,可以從以下幾個(gè)角度來(lái)分析氯析。(業(yè)務(wù)驅(qū)動(dòng)技術(shù))

  • 任務(wù)的性質(zhì)
    CPU密集型任務(wù)亏较、IO密集型任務(wù)和混合型任務(wù)。
  • 任務(wù)的優(yōu)先級(jí)
    高掩缓、中和低雪情。
  • 任務(wù)的執(zhí)行時(shí)間
    長(zhǎng)、中和短你辣。
  • 任務(wù)的依賴性
    是否依賴其他系統(tǒng)資源巡通,如數(shù)據(jù)庫(kù)連接。
  • 建議
    建議使用有界隊(duì)列舍哄。有界隊(duì)列能增加系統(tǒng)的穩(wěn)定性和預(yù)警能力宴凉,可以根據(jù)需要設(shè)大一點(diǎn)兒,比如幾千表悬。

線程池的監(jiān)控

可以通過(guò)線程池提供的參數(shù)進(jìn)行監(jiān)控弥锄,在監(jiān)控線程池的時(shí)候可以使用以下屬性。

  • taskCount
    線程池需要執(zhí)行的任務(wù)數(shù)量
  • completedTaskCount
    線程池在運(yùn)行過(guò)程中已完成的任務(wù)數(shù)量蟆沫,小于或等于taskCount籽暇。
  • largestPoolSize
    線程池里曾經(jīng)創(chuàng)建過(guò)的最大線程數(shù)量。通過(guò)這個(gè)數(shù)據(jù)可以知道線程池是否曾經(jīng)滿過(guò)饭庞。如該數(shù)值等于線程池的最大大小戒悠,則表示線程池曾經(jīng)滿過(guò)救崔。
  • getPoolSize
    線程池的線程數(shù)量捏顺。如果線程池不銷毀的話,線程池里的線程不會(huì)自動(dòng)銷毀幅骄,所以這個(gè)大小只增不減拆座。
  • getActiveCount
    獲取活動(dòng)的線程數(shù)。通過(guò)擴(kuò)展線程池進(jìn)行監(jiān)控孕索□锾迹可以通過(guò)繼承線程池來(lái)自定義線程池,重寫(xiě)線程池的beforeExecute肄渗、
    afterExecute和terminated方法翎嫡,也可以在任務(wù)執(zhí)行前永乌、執(zhí)行后和線程池關(guān)閉前執(zhí)行一些代碼來(lái)進(jìn)行監(jiān)控。例如硝桩,監(jiān)控任務(wù)的平均執(zhí)行時(shí)間碗脊、最大執(zhí)行時(shí)間和最小執(zhí)行時(shí)間等橄妆。這幾個(gè)方法在線程池里是空方法。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末矢劲,一起剝皮案震驚了整個(gè)濱河市芬沉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹋艺,老刑警劉巖黄刚,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涛救,死亡現(xiàn)場(chǎng)離奇詭異业扒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蹭沛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)致板,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)斟或,“玉大人集嵌,你說(shuō)我怎么就攤上這事×洌” “怎么了凤粗?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵嫌拣,是天一觀的道長(zhǎng)异逐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)腥例,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任璃弄,我火速辦了婚禮底瓣,結(jié)果婚禮上蕉陋,老公的妹妹穿的比我還像新娘。我一直安慰自己茁肠,他們只是感情好缩举,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布仅孩。 她就那樣靜靜地躺著,像睡著了一般京腥。 火紅的嫁衣襯著肌膚如雪溅蛉。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,736評(píng)論 1 312
  • 那天欠气,我揣著相機(jī)與錄音预柒,去河邊找鬼。 笑死宜鸯,一個(gè)胖子當(dāng)著我的面吹牛围段,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播适贸,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拜姿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蕊肥?” 一聲冷哼從身側(cè)響起壁却,我...
    開(kāi)封第一講書(shū)人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤展东,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后盐肃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年耘成,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荷辕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡控嗜,死狀恐怖疆栏,靈堂內(nèi)的尸體忽然破棺而出惫谤,到底是詐尸還是另有隱情,我是刑警寧澤溜歪,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布蝴猪,位于F島的核電站膊爪,受9級(jí)特大地震影響米酬,放射性物質(zhì)發(fā)生泄漏趋箩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一跳芳、第九天 我趴在偏房一處隱蔽的房頂上張望筛严。 院中可真熱鬧醉旦,春花似錦、人聲如沸檬输。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至簇搅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吟税,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留备典,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓欲险,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親天试。 傳聞我的和親對(duì)象是個(gè)殘疾皇子然低,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

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