五一疯、線程池
在我們的開發(fā)中經(jīng)常會使用到多線程撼玄。例如在Android中,由于主線程的諸多限制墩邀,像網(wǎng)絡(luò)請求等一些耗時(shí)的操作我們必須在子線程中運(yùn)行掌猛。我們往往會通過new Thread來開啟一個(gè)子線程,待子線程操作完成以后通過Handler切換到主線程中運(yùn)行眉睹。這么以來我們無法管理我們所創(chuàng)建的子線程荔茬,并且無限制的創(chuàng)建子線程,它們相互之間競爭辣往,很有可能由于占用過多資源而導(dǎo)致死機(jī)或者OOM兔院。所以在Java中為我們提供了線程池來管理我們所創(chuàng)建的線程。
為什么要使用多線程站削?
-
更多的處理器核心
線程是大多數(shù)操作系統(tǒng)調(diào)度的基本單元坊萝,一個(gè)程序作為一個(gè)進(jìn)程來運(yùn)行,程序運(yùn)行過程中能夠創(chuàng)建多個(gè)線程许起,而一個(gè)線程在一個(gè)時(shí)刻只能運(yùn)行在一個(gè)處理器核心上十偶,采用多線程技術(shù),將計(jì)算邏輯分配到多個(gè)處理器核心上园细,就會顯示減少程序的處理時(shí)間惦积,而現(xiàn)在計(jì)算機(jī)都是多核處理器,隨著核心的增加而變得更加有效率
-
更快的響應(yīng)時(shí)間
在編寫一些較為復(fù)雜的代碼(指的是復(fù)雜的邏輯)猛频,例如狮崩,一筆訂單的創(chuàng)建,包括訂單的數(shù)據(jù)插入鹿寻、生成訂單快照睦柴、通知賣家和處理貨品銷售數(shù)量等,我們可以使用多線程技術(shù)毡熏,將一些操作性不強(qiáng)的操作(生成訂單坦敌、通知賣家)派發(fā)給其他線程來處理。這樣的好處是響應(yīng)用戶請求的線程能夠盡快地處理完成痢法,縮短響應(yīng)時(shí)間狱窘,提升用戶體驗(yàn)
-
更好的編程模型
Java為多線程編程提供了良好、考究并且一致的編程模型财搁,使開發(fā)人員能夠更加專注于問題的解決蘸炸,為遇到的問題建立合適的模型,而不是考慮如何將其多線程化
線程池的優(yōu)勢
重用線程池中的線程妇拯,避免因?yàn)榫€程創(chuàng)建和銷毀所帶的性能開銷
能夠有效控制線程池的最大并發(fā)數(shù)幻馁,避免大量的線程之間因?yàn)榛ハ鄵屨枷到y(tǒng)資源而導(dǎo)致的阻塞現(xiàn)象
能夠?qū)€程進(jìn)行簡單的管理洗鸵,并提供定時(shí)執(zhí)行以及指定間隔循環(huán)執(zhí)行功能
線程池的使用
-
使用步驟
// 創(chuàng)建線程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); // 向線程池提交任務(wù) threadPool.execute(new Runnable() { @Override public void run() { ... // 線程執(zhí)行的任務(wù) } }); // 關(guān)閉線程池 threadPool.shutdown(); // 設(shè)置線程池的狀態(tài)為SHUTDOWN越锈,然后中斷所有沒有正在執(zhí)行任務(wù)的線程 threadPool.shutdownNow(); // 設(shè)置線程池的狀態(tài)為 STOP仗嗦,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表
-
七大參數(shù)
-
corePoolSize
線程池中的核心線程數(shù)甘凭,默認(rèn)情況下稀拐,核心線程一直存活在線程池中,即便他們在線程池中處于閑置狀態(tài)丹弱。除非我們將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)為true的時(shí)候德撬,這時(shí)候處于閑置的核心線程在等待新任務(wù)到來時(shí)會有超時(shí)策略,這個(gè)超時(shí)時(shí)間由keepAliveTime來指定躲胳。一旦超過所設(shè)置的超時(shí)時(shí)間蜓洪,閑置的核心線程就會被終止。
-
maximumPoolSize
線程池中所容納的最大線程數(shù)坯苹,如果活動的線程達(dá)到這個(gè)數(shù)值以后隆檀,后續(xù)的新任務(wù)將會被阻塞。包含核心線程數(shù)+非核心線程數(shù)粹湃。
-
keepAliveTime
非核心線程閑置時(shí)的超時(shí)時(shí)長恐仑,對于非核心線程,閑置時(shí)間超過這個(gè)時(shí)間为鳄,非核心線程就會被回收裳仆。只有對ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)為true的時(shí)候,這個(gè)超時(shí)時(shí)間才會對核心線程產(chǎn)生效果孤钦。
-
TimeUnit
用于指定keepAliveTime參數(shù)的時(shí)間單位歧斟。他是一個(gè)枚舉,可以使用的單位有天(TimeUnit.DAYS)偏形,小時(shí)(TimeUnit.HOURS)静袖,分鐘(TimeUnit.MINUTES),毫秒(TimeUnit.MILLISECONDS)壳猜,微秒(TimeUnit.MICROSECONDS, 千分之一毫秒)和毫微秒(TimeUnit.NANOSECONDS, 千分之一微秒)勾徽;
-
workQueue
線程池中保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列。通過線程池中的execute方法提交的Runable對象都會存儲在該隊(duì)列中统扳。
-
ThreadFactory
線程工廠喘帚,為線程池提供新線程的創(chuàng)建。ThreadFactory是一個(gè)接口咒钟,里面只有一個(gè)newThread方法吹由。 默認(rèn)為DefaultThreadFactory類
-
RejectedExecutionHandler
飽和策略
-
-
執(zhí)行流程
https://note.youdao.com/yws/public/resource/7ad083d3cb19a5c9a95bc4a3445ca9d7/xmlnote/2436B2BC3E464E97A1A3319A9087D553/4783提交任務(wù)后,如果線程池的線程數(shù)未達(dá)到核心線程數(shù)朱嘴,則創(chuàng)建核心線程處理任務(wù)
如果線程數(shù)大于或等于核心線程數(shù)倾鲫,則將任務(wù)加入到任務(wù)隊(duì)列中粗合,線程池中的空閑線程會不斷地從任務(wù)隊(duì)列中取出任務(wù)進(jìn)行處理。
如果任務(wù)隊(duì)列已經(jīng)滿了乌昔,并且線程數(shù)沒有達(dá)到最大的線程數(shù)隙疚,則會創(chuàng)建非核心線程去處理任務(wù)
如果線程數(shù)超過了最大的線程數(shù),則執(zhí)行飽和策略磕道。
-
常用的線程池
-
定長線程池(FixedThreadPool)
它是一種線程數(shù)量固定的線程池供屉,當(dāng)線程處于空閑狀態(tài)時(shí),它們并不會被回收溺蕉,除非線程池關(guān)閉了伶丐。當(dāng)所有線程都處于活動狀態(tài)時(shí),新任務(wù)都會處于等待狀態(tài)疯特,直到有線程空閑出來哗魂。由于FixedThreadPool只有核心線程并且這些核心線程不會被回收,這意味能夠更加快速的響應(yīng)外界的請求漓雅。核心線程沒有超時(shí)機(jī)制录别,另外任務(wù)隊(duì)列沒有大小限制(LinkedBlockingQueue<Runnable>)。
特點(diǎn):只有核心線程故硅,線程數(shù)量固定庶灿,任務(wù)隊(duì)列為鏈表結(jié)構(gòu)的有界隊(duì)列。
應(yīng)用場景:控制線程最大并發(fā)數(shù)
-
定時(shí)線程池(ScheduledThreadPool )
主要用于執(zhí)行定時(shí)任務(wù)和具有固定周期的重復(fù)任務(wù)吃衅。核心線程數(shù)固定往踢,非核心線程數(shù)沒有限制,并且當(dāng)非核心線程閑置時(shí)會立即被回收徘层。任務(wù)隊(duì)列為延時(shí)阻塞隊(duì)列(DelayedWorkQueue<RunnableScheduledFuture>),Future為任務(wù)的包裝類
特點(diǎn):核心線程數(shù)量固定峻呕,非核心線程數(shù)量無限,執(zhí)行完閑置10ms后回收趣效,任務(wù)隊(duì)列為延時(shí)阻塞隊(duì)列瘦癌。
應(yīng)用場景:執(zhí)行定時(shí)或周期性的任務(wù)。
-
可緩存線程池(CachedThreadPool)
它是一個(gè)線程數(shù)量不定的線程池跷敬,只有非核心線程讯私,并且最大線程數(shù)為Integer.MAX_VALUE,實(shí)際上就相當(dāng)于最大線程數(shù)量可以任意大西傀。當(dāng)線程中的線程都處于活躍狀態(tài)時(shí)斤寇,線程池就會創(chuàng)建新的線程來處理任務(wù),否則就會利用空閑的線程來處理新任務(wù)拥褂。線程池中的線程都有超時(shí)機(jī)制娘锁,60秒,超過閑置時(shí)間就會被回收饺鹃。使用的任務(wù)隊(duì)列為不存儲元素的SynchronousQueue<Runnable>莫秆,相當(dāng)于一個(gè)空集合间雀,這就導(dǎo)致任何線程都會立刻被執(zhí)行。當(dāng)整個(gè)線程池都屬于空閑狀態(tài)時(shí)镊屎,線程池中的線程都會超時(shí)而停止惹挟,這個(gè)時(shí)候池中相當(dāng)于是沒有線程的,幾乎不會占有任何系統(tǒng)資源的杯道。
特點(diǎn):無核心線程匪煌,非核心線程數(shù)量無限责蝠,執(zhí)行完閑置60s后回收党巾,任務(wù)隊(duì)列為不存儲元素的阻塞隊(duì)列。
應(yīng)用場景:執(zhí)行大量霜医、耗時(shí)少的任務(wù)齿拂。
-
單線程化線程池(SingleThreadExecutor)
內(nèi)部只有一個(gè)核心線程,它確保所有任務(wù)都在同一個(gè)線程中按順序執(zhí)行肴敛,SingleThreadExecutor存在的意義在于統(tǒng)一所有的外界任務(wù)到一個(gè)線程中署海,使得這些任務(wù)不需要處理線程同步問題。任務(wù)隊(duì)列為LinkedBlockingQueue<Runnable>
特點(diǎn):只有1個(gè)核心線程医男,無非核心線程砸狞,執(zhí)行完立即回收,任務(wù)隊(duì)列為鏈表結(jié)構(gòu)的有界隊(duì)列镀梭。
應(yīng)用場景:不適合并發(fā)但可能引起IO阻塞性及影響UI線程響應(yīng)的操作刀森,如數(shù)據(jù)庫操作、文件操作等报账。
對比:
https://note.youdao.com/yws/public/resource/7ad083d3cb19a5c9a95bc4a3445ca9d7/xmlnote/C96ECA80F051483E8C07D7E67156825F/3114補(bǔ)充:
-
阻塞隊(duì)列
-
ArrayBlockingQueue
基于數(shù)組實(shí)現(xiàn)的有界的阻塞隊(duì)列,該隊(duì)列按照FIFO(先進(jìn)先出)原則對隊(duì)列中的元素進(jìn)行排序研底。
-
LinkedBlockingQueue
基于鏈表實(shí)現(xiàn)的有界阻塞隊(duì)列,該隊(duì)列按照FIFO(先進(jìn)先出)原則對隊(duì)列中的元素進(jìn)行排序透罢,容量默認(rèn)為Integer.MAX_VALUE榜晦。當(dāng)線程池中線程數(shù)量達(dá)到corePoolSize后,再有新任務(wù)進(jìn)來羽圃,會一直存入該隊(duì)列乾胶,而不會去創(chuàng)建新線程直到maxPoolSize(幾乎不可能達(dá)到),因此使用該工作隊(duì)列時(shí)朽寞,參數(shù)maxPoolSize其實(shí)是不起作用的识窿。
-
SynchronousQueue
內(nèi)部沒有任何容量的阻塞隊(duì)列。在它內(nèi)部沒有任何的緩存空間愁憔。對于SynchronousQueue中的數(shù)據(jù)元素只有當(dāng)我們試著取走的時(shí)候才可能存在腕扶,每一個(gè)put必須等待一個(gè)take操作
-
PriorityBlockingQueue
具有優(yōu)先級的無限阻塞隊(duì)列。
-
DelayQueue
是支持延時(shí)獲取元素的無界阻塞隊(duì)列吨掌。要求元素都實(shí)現(xiàn)Delayed接口半抱,通過執(zhí)行延時(shí)從隊(duì)列中提取任務(wù)脓恕,時(shí)間沒到任務(wù)取不出來。
-
FixedThreadPool和SingleThreadExecutor:主要問題是堆積的請求處理隊(duì)列均采用LinkedBlockingQueue窿侈,可能會耗費(fèi)非常大的內(nèi)存炼幔,甚至OOM。
-
-
如何配置參數(shù)
《Java線程池實(shí)現(xiàn)原理及其在美團(tuán)業(yè)務(wù)中的實(shí)踐》
需要針對具體情況而具體處理史简,不同的任務(wù)類別應(yīng)采用不同規(guī)模的線程池乃秀,任務(wù)類別可劃分為CPU密集型任務(wù)、IO密集型任務(wù)和混合型任務(wù)圆兵。(N代表CPU個(gè)數(shù))
-
cpu密集型任務(wù)
這種任務(wù)消耗的主要是 CPU 資源跺讯,可以將線程數(shù)設(shè)置為 N(CPU 核心數(shù))+1,比 CPU 核心數(shù)多出來的一個(gè)線程是為了防止線程偶發(fā)的缺頁中斷殉农,或者其它原因?qū)е碌娜蝿?wù)暫停而帶來的影響刀脏。一旦任務(wù)暫停,CPU 就會處于空閑狀態(tài)超凳,而在這種情況下多出來的一個(gè)線程就可以充分利用 CPU 的空閑時(shí)間愈污。
當(dāng)線程數(shù)量太小,同一時(shí)間大量請求將被阻塞在線程隊(duì)列中排隊(duì)等待執(zhí)行線程轮傍,此時(shí) CPU 沒有得到充分利用暂雹;當(dāng)線程數(shù)量太大,被創(chuàng)建的執(zhí)行線程同時(shí)在爭取 CPU 資源创夜,又會導(dǎo)致大量的上下文切換杭跪,從而增加線程的執(zhí)行時(shí)間,影響了整體執(zhí)行效率挥下。通過測試可知揍魂,4~6 個(gè)線程數(shù)是最合適的。
-
io密集型任務(wù)
這種任務(wù)應(yīng)用起來棚瘟,系統(tǒng)會用大部分的時(shí)間來處理 I/O 交互现斋,而線程在處理 I/O 的時(shí)間段內(nèi)不會占用 CPU 來處理,這時(shí)就可以將 CPU 交出給其它線程使用偎蘸。因此在 I/O 密集型任務(wù)的應(yīng)用中庄蹋,我們可以多配置一些線程,具體的計(jì)算方法是 2N限书。
當(dāng)線程數(shù)量在 8 時(shí),線程平均執(zhí)行時(shí)間是最佳的倦西,這個(gè)線程數(shù)量和我們的計(jì)算公式所得的結(jié)果就差不多赁严。
-
混合型
可以拆分為CPU密集型任務(wù)和IO密集型任務(wù)粉铐,當(dāng)這兩類任務(wù)執(zhí)行時(shí)間相差無幾時(shí)卤档,通過拆分再執(zhí)行的吞吐率高于串行執(zhí)行的吞吐率,但若這兩類任務(wù)執(zhí)行時(shí)間有數(shù)據(jù)級的差距劝枣,那么沒有拆分的意義汤踏。
線程池的核心線程數(shù)=(線程等待時(shí)間/線程CPU時(shí)間+1)*CPU核心數(shù);
-
快速響應(yīng)用戶請求
從用戶體驗(yàn)角度看舔腾,這個(gè)結(jié)果響應(yīng)的越快越好溪胶,如果一個(gè)頁面半天都刷不出,用戶可能就放棄查看這個(gè)商品了琢唾。而面向用戶的功能聚合通常非常復(fù)雜载荔,伴隨著調(diào)用與調(diào)用之間的級聯(lián)、多級級聯(lián)等情況采桃,業(yè)務(wù)開發(fā)同學(xué)往往會選擇使用線程池這種簡單的方式,將調(diào)用封裝成任務(wù)并行的執(zhí)行丘损,縮短總體響應(yīng)時(shí)間普办。另外,使用線程池也是有考量的徘钥,這種場景最重要的就是獲取最大的響應(yīng)速度去滿足用戶衔蹲,所以應(yīng)該不設(shè)置隊(duì)列去緩沖并發(fā)任務(wù),調(diào)高corePoolSize和maxPoolSize去盡可能創(chuàng)造多的線程快速執(zhí)行任務(wù)呈础。
-
快速處理批量任務(wù)
這種場景需要執(zhí)行大量的任務(wù)舆驶,我們也會希望任務(wù)執(zhí)行的越快越好。這種情況下而钞,也應(yīng)該使用多線程策略沙廉,并行計(jì)算。但與響應(yīng)速度優(yōu)先的場景區(qū)別在于臼节,這類場景任務(wù)量巨大撬陵,并不需要瞬時(shí)的完成,而是關(guān)注如何使用有限的資源网缝,盡可能在單位時(shí)間內(nèi)處理更多的任務(wù)巨税,也就是吞吐量優(yōu)先的問題。所以應(yīng)該設(shè)置隊(duì)列去緩沖并發(fā)任務(wù)粉臊,調(diào)整合適的corePoolSize去設(shè)置處理任務(wù)的線程數(shù)草添。在這里,設(shè)置的線程數(shù)過多可能還會引發(fā)線程上下文切換頻繁的問題扼仲,也會降低處理任務(wù)的速度远寸,降低吞吐量促王。
我們要提高線程池的處理能力蝇狼,一定要先保證一個(gè)合理的線程數(shù)量倡怎,也就是保證 CPU 處理線程的最大化。在此前提下颤专,我們再增大線程池隊(duì)列栖秕,通過隊(duì)列將來不及處理的線程緩存起來簇捍。在設(shè)置緩存隊(duì)列時(shí)暑塑,我們要盡量使用一個(gè)有界隊(duì)列锅必,以防因隊(duì)列過大而導(dǎo)致的內(nèi)存溢出問題搞隐。
-
Java線程池ThreadPoolExecutor八大拒絕策略淺析
內(nèi)置:4個(gè)
-
AbortPolicy(中止策略)
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } } 功能:當(dāng)觸發(fā)拒絕策略時(shí)逢捺,直接拋出拒絕執(zhí)行的異常蒸甜,中止策略的意思也就是打斷當(dāng)前執(zhí)行流程 使用場景:這個(gè)就沒有特殊的場景了柠新,但是一點(diǎn)要正確處理拋出的異常恨憎。 ThreadPoolExecutor中默認(rèn)的策略就是AbortPolicy憔恳,ExecutorService接口的系列ThreadPoolExecutor因?yàn)槎紱]有顯示的設(shè)置拒絕策略钥组,所以默認(rèn)的都是這個(gè)程梦。 但是請注意屿附,ExecutorService中的線程池實(shí)例隊(duì)列都是無界的挺份,也就是說把內(nèi)存撐爆了都不會觸發(fā)拒絕策略匀泊。當(dāng)自己自定義線程池實(shí)例時(shí)探赫,使用這個(gè)策略一定要處理好觸發(fā)策略時(shí)拋的異常,因?yàn)樗麜驍喈?dāng)前的執(zhí)行流程。
-
CallerRunsPolicy(調(diào)用者運(yùn)行策略)
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } } 功能:當(dāng)觸發(fā)拒絕策略時(shí)毛仪,只要線程池沒有關(guān)閉箱靴,就由提交任務(wù)的當(dāng)前線程處理衡怀。 使用場景:一般在不允許失敗的抛杨、對性能要求不高怖现、并發(fā)量較小的場景下使用潘拨,因?yàn)榫€程池一般情況下不會關(guān)閉铁追,也就是提交的任務(wù)一定會被運(yùn)行琅束,但是由于是調(diào)用者線程自己執(zhí)行的透硝,當(dāng)多次提交任務(wù)時(shí)濒生,就會阻塞后續(xù)任務(wù)執(zhí)行罪治,性能和效率自然就慢了觉义。
-
DiscardOldestPolicy(棄老策略)
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } } 功能:如果線程池未關(guān)閉晒骇,就彈出隊(duì)列頭部的元素洪囤,然后嘗試執(zhí)行當(dāng)前任務(wù) 使用場景:這個(gè)策略還是會丟棄任務(wù)瘤缩,丟棄時(shí)也是毫無聲息剥啤,但是特點(diǎn)是丟棄的是老的未執(zhí)行的任務(wù),而且是待執(zhí)行優(yōu)先級較高的任務(wù)刻诊。 基于這個(gè)特性坏逢,我能想到的場景就是,發(fā)布消息肖揣,和修改消息龙优,當(dāng)消息發(fā)布出去后彤断,還未執(zhí)行宰衙,此時(shí)更新的消息又來了供炼,這個(gè)時(shí)候未執(zhí)行的消息的版本比現(xiàn)在提交的消息版本要低就可以被丟棄了袋哼。因?yàn)殛?duì)列中還有可能存在消息版本更低的消息會排隊(duì)執(zhí)行闸衫,所以在真正處理消息的時(shí)候一定要做好消息的版本比較蔚出。
-
DiscardPolicy(丟棄策略)
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } } 功能:直接靜悄悄的丟棄這個(gè)任務(wù)衅胀,不觸發(fā)任何動作 使用場景:如果你提交的任務(wù)無關(guān)緊要酥筝,你就可以使用它 嘿歌。因?yàn)樗褪莻€(gè)空實(shí)現(xiàn)宙帝,會悄無聲息的吞噬你的的任務(wù)步脓。所以這個(gè)策略基本上不用了
第三方實(shí)現(xiàn)的拒絕策略
-
dubbo[?d?b??]中的線程拒絕策略
public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class); private final String threadName; private final URL url; private static volatile long lastPrintTime = 0; private static Semaphore guard = new Semaphore(1); public AbortPolicyWithReport(String threadName, URL url) { this.threadName = threadName; this.url = url; } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { String msg = String.format("Thread pool is EXHAUSTED!" + " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," + " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!", threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(), e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), url.getProtocol(), url.getIp(), url.getPort()); logger.warn(msg); dumpJStack(); throw new RejectedExecutionException(msg); } private void dumpJStack() { //省略實(shí)現(xiàn) } } 可以看到,當(dāng)dubbo的工作線程觸發(fā)了線程拒絕后鸳君,主要做了三個(gè)事情,原則就是盡量讓使用者清楚觸發(fā)線程拒絕策略的真實(shí)原因砸紊。 1)輸出了一條警告級別的日志醉顽,日志內(nèi)容為線程池的詳細(xì)設(shè)置參數(shù)游添,以及線程池當(dāng)前的狀態(tài)否淤,還有當(dāng)前拒絕任務(wù)的一些詳細(xì)信息石抡≈螅可以說嗡贺,這條日志诫睬,使用dubbo的有過生產(chǎn)運(yùn)維經(jīng)驗(yàn)的或多或少是見過的摄凡,這個(gè)日志簡直就是日志打印的典范,其他的日志打印的典范還有spring钦扭。得益于這么詳細(xì)的日志其弊,可以很容易定位到問題所在 2)輸出當(dāng)前線程堆棧詳情膀斋,這個(gè)太有用了概页,當(dāng)你通過上面的日志信息還不能定位問題時(shí)技掏,案發(fā)現(xiàn)場的dump線程上下文信息就是你發(fā)現(xiàn)問題的救命稻草哑梳。 3)繼續(xù)拋出拒絕執(zhí)行異常鸠真,使本次任務(wù)失敗吠卷,這個(gè)繼承了JDK默認(rèn)拒絕策略的特性
-
Netty中的線程池拒絕策略
private static final class NewThreadRunsPolicy implements RejectedExecutionHandler { NewThreadRunsPolicy() { super(); } public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { final Thread t = new Thread(r, "Temporary task executor"); t.start(); } catch (Throwable e) { throw new RejectedExecutionException( "Failed to start a new thread", e); } } } Netty中的實(shí)現(xiàn)很像JDK中的CallerRunsPolicy祭隔,舍不得丟棄任務(wù)疾渴。不同的是搞坝,CallerRunsPolicy是直接在調(diào)用者線程執(zhí)行的任務(wù)桩撮。而 Netty是新建了一個(gè)線程來處理的距境。 所以,Netty的實(shí)現(xiàn)相較于調(diào)用者執(zhí)行策略的使用面就可以擴(kuò)展到支持高效率高性能的場景了粟按。但是也要注意一點(diǎn),Netty的實(shí)現(xiàn)里疼鸟,在創(chuàng)建線程時(shí)未做任何的判斷約束,也就是說只要系統(tǒng)還有資源就會創(chuàng)建新的線程來處理捌朴,直到new不出新的線程了洼怔,才會拋創(chuàng)建線程失敗的異常镣隶。
-
activeMq中的線程池拒絕策略
new RejectedExecutionHandler() { @Override public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { try { executor.getQueue().offer(r, 60, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new RejectedExecutionException("Interrupted waiting for BrokerService.worker"); } throw new RejectedExecutionException("Timed Out while attempting to enqueue Task."); } }); activeMq中的策略屬于最大努力執(zhí)行任務(wù)型,當(dāng)觸發(fā)拒絕策略時(shí)域那,在嘗試一分鐘的時(shí)間重新將任務(wù)塞進(jìn)任務(wù)隊(duì)列琉雳,當(dāng)一分鐘超時(shí)還沒成功時(shí)友瘤,就拋出異常
-
pinpoint中的線程池拒絕策略
public class RejectedExecutionHandlerChain implements RejectedExecutionHandler { private final RejectedExecutionHandler[] handlerChain; public static RejectedExecutionHandler build(List<RejectedExecutionHandler> chain) { Objects.requireNonNull(chain, "handlerChain must not be null"); RejectedExecutionHandler[] handlerChain = chain.toArray(new RejectedExecutionHandler[0]); return new RejectedExecutionHandlerChain(handlerChain); } private RejectedExecutionHandlerChain(RejectedExecutionHandler[] handlerChain) { this.handlerChain = Objects.requireNonNull(handlerChain, "handlerChain must not be null"); } @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { for (RejectedExecutionHandler rejectedExecutionHandler : handlerChain) { rejectedExecutionHandler.rejectedExecution(r, executor); } } } pinpoint的拒絕策略實(shí)現(xiàn)很有特點(diǎn)翠肘,和其他的實(shí)現(xiàn)都不同。他定義了一個(gè)拒絕策略鏈辫秧,包裝了一個(gè)拒絕策略列表香伴,當(dāng)觸發(fā)拒絕策略時(shí),會將策略鏈中的rejectedExecution依次執(zhí)行一遍乍钻。
-