一.線程池和相關(guān)參數(shù)概述
線程池,就是存放線程的池子,池子里存放了很多可以復(fù)用的線程。
管理線程,當(dāng)線程執(zhí)行完當(dāng)前任務(wù),不會(huì)死掉而是 會(huì)從隊(duì)列里面取
通過線程復(fù)用機(jī)制,并對(duì)線程進(jìn)行統(tǒng)一管理,具有以下優(yōu)點(diǎn):
1.降低系統(tǒng)資源消耗。通過復(fù)用已存在的線程,降低線程創(chuàng)建和銷毀造成的消耗;
2.提高響應(yīng)速度。當(dāng)有任務(wù)到達(dá)時(shí),無需等待新線程的創(chuàng)建便能立即執(zhí)行;
3.提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會(huì)消耗大量系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行對(duì)線程進(jìn)行統(tǒng)一的分配、調(diào)優(yōu)和監(jiān)控。
本文主要是圍繞ThreadPoolExecutor(線程池框架的核心類)的構(gòu)造方法參數(shù)展開:
1.corePoolSize
線程池中的核心線程數(shù)。當(dāng)提交一個(gè)任務(wù)時(shí),線程池創(chuàng)建一個(gè)新線程執(zhí)行任務(wù),直到當(dāng)前線程數(shù)等于corePoolSize;如果當(dāng)前線程數(shù)為corePoolSize,繼續(xù)提交的任務(wù)被保存到阻塞隊(duì)列中,等待被執(zhí)行。
2.maximumPoolSize
額外最大線程數(shù)。上面說到任務(wù)數(shù)足夠多,且使用的是有界隊(duì)列,如果當(dāng)前阻塞隊(duì)列滿了,且繼續(xù)提交任務(wù),則創(chuàng)建新的線程執(zhí)行任務(wù),首先從隊(duì)列里面取,如果隊(duì)列里面的消息執(zhí)行完畢,等下一定時(shí)間,額外線程自動(dòng)銷毀。
3.keepAliveTime
線程空閑時(shí)的存活時(shí)間。默認(rèn)情況下,可以理解成額外最大線程數(shù)沒活干了,額外線程線程空閑的時(shí)間達(dá)到keepAliveTime,則會(huì)終止,直到線程池中的線程數(shù)不超過corePoolSize。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法,keepAliveTime參數(shù)也會(huì)起作用,直到線程池中的線程數(shù)為0。
4.unit
keepAliveTime參數(shù)的時(shí)間單位。
5.workQueue
任務(wù)緩存隊(duì)列,用來存放等待執(zhí)行的任務(wù)。如果當(dāng)前線程數(shù)為corePoolSize,繼續(xù)提交的任務(wù)就會(huì)被保存到任務(wù)緩存隊(duì)列中,等待被執(zhí)行。
一般來說,這里的BlockingQueue有以下三種選擇:
* SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列,每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài)。因此,如果線程池中始終沒有空閑線程(任務(wù)提交的平均速度快于被處理的速度),可能出現(xiàn)無限制的線程增長(zhǎng)。
* LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,如果不設(shè)置初始化容量,其容量為Integer.MAX_VALUE,即為無界隊(duì)列。因此,如果線程池中線程數(shù)達(dá)到了corePoolSize,且始終沒有空閑線程(任務(wù)提交的平均速度快于被處理的速度),任務(wù)緩存隊(duì)列可能出現(xiàn)無限制的增長(zhǎng)。
* ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,按FIFO排序任務(wù)。
6.threadFactory
線程工廠,創(chuàng)建新線程時(shí)使用的線程工廠。
7.handler
任務(wù)拒絕策略,當(dāng)阻塞隊(duì)列滿了,且線程池中的線程數(shù)達(dá)到maximumPoolSize,如果繼續(xù)提交任務(wù),就會(huì)采取任務(wù)拒絕策略處理該任務(wù),線程池提供了4種任務(wù)拒絕策略:
* AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常,默認(rèn)策略;
* CallerRunsPolicy:由調(diào)用execute方法的線程執(zhí)行該任務(wù);
* DiscardPolicy:丟棄任務(wù),但是不拋出異常;
* DiscardOldestPolicy:丟棄阻塞隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)。
* 當(dāng)然也可以根據(jù)應(yīng)用場(chǎng)景實(shí)現(xiàn)RejectedExecutionHandler接口,自定義飽和策略,如記錄日志或持久化存儲(chǔ)不能處理的任務(wù)。
總結(jié)下上訴參數(shù):假設(shè)corePoolSize為10 ,maximumPoolSize為10,線程空閑時(shí)的存活時(shí)間為60s,隊(duì)列采用的是有界隊(duì)列ArrayBlockingQueue 設(shè)置閾值200,使用拒絕策略 , 當(dāng)前2000個(gè)任務(wù)提交過來 流程如下圖

參數(shù)案例描述:
? ??????當(dāng)前2000筆 任務(wù)進(jìn)來,10個(gè)核心線程去處理,剩下的1990任務(wù)隊(duì)列里面放200個(gè)。剩下的1790個(gè)任務(wù)。隊(duì)列塞滿會(huì)去創(chuàng)建10個(gè)額外線程和核心線程一起去 去執(zhí)行剩下的1780個(gè)任務(wù)。當(dāng)還有剩下任務(wù)處理不了就會(huì)觸發(fā)任務(wù)拒絕策略。??
? ? ? ? 當(dāng)前220筆 任務(wù)進(jìn)來,10個(gè)核心線程去處理,剩下的210任務(wù)隊(duì)列里面放200個(gè)。剩下的10個(gè)任務(wù)。隊(duì)列塞滿會(huì)去創(chuàng)建10個(gè)額外線程 去執(zhí)行隊(duì)列放不下的任務(wù)。當(dāng)額外線程和核心線程處理完隊(duì)列里面的隊(duì)列。沒有任務(wù)可執(zhí)行時(shí),額外線程會(huì)等待我們?cè)O(shè)置的keepAliveTime,還是沒有任務(wù)的情況下,就會(huì)被回收了。
以上是絕對(duì)理想的狀況下。
二.常用線程池使用方案和相關(guān)問題?
1.newFixedThreadPool線程池?(固定大小線程池)

由參數(shù)可知 核心線程 和額外線程值是相同的,額外線程被回收時(shí)間是0,采用的是無界隊(duì)列。默認(rèn)采用的拒絕策略為AbortPolicy。分析得 核心線程和額外線程處理不過來得情況,會(huì)一直往隊(duì)列里面放任務(wù)。
可能存在的問題:隊(duì)列過大 導(dǎo)致內(nèi)存溢出 OOM
2.將額外線程設(shè)置成最大

當(dāng)任務(wù)量足夠大,超過隊(duì)列。交由額外線程處理。就會(huì)創(chuàng)建過多線程。
可能存在問題:特殊場(chǎng)景下,線程過多可能會(huì)導(dǎo)致系統(tǒng)奔潰。cpu負(fù)載過高。
3.解決方案
1.具體解決方案 根據(jù)業(yè)務(wù)系統(tǒng)而定:
? ??????華瑞批量查證舉例:定時(shí)任務(wù)CZJZRW001每隔2min 輪詢一次 會(huì)從業(yè)務(wù)表verifycationTask 中 查詢出待處理和處理中的狀態(tài)的任務(wù) 根據(jù)表中的查證類型 分流到具體的反欺詐異步查證 ,還款查證,充值查證,貸款查證。具體查證根據(jù)處理結(jié)果更新verifycationTask表查證狀態(tài)。處理成功 或者失敗的定時(shí)任務(wù)無法再次輪詢。這樣就不需要考慮以上場(chǎng)景。使用線程池的情況下核心線程,額外線程處理不過來且隊(duì)列已滿使用DiscardPolicy拒絕不拋異常策略?,即可滿足該業(yè)務(wù)場(chǎng)景。類結(jié)構(gòu)如下圖

2.思路
可以實(shí)現(xiàn)RejectedExecutionHandler接口 自定義拒絕策略? 將被拒絕的任務(wù)信息緩存到磁盤,等待線程池負(fù)載較低 從磁盤讀取重新提交到任務(wù)里面去執(zhí)行