詳解Java?ThreadPoolExecutor的拒絕策略
背景
線程池的技術(shù)在項(xiàng)目中使用廣泛,線程池提供了四種拒絕策略,大家是否了解這四種拒絕的策略呢?本文將詳細(xì)的講解ThreadPoolExecutor的四種拒絕策略,以及相關(guān)的注意事項(xiàng)。
線程池基本原理
線程池的原理如下圖:
說明:
- 當(dāng)前運(yùn)行的線程少于corePoolSize,則創(chuàng)建新線程來執(zhí)行任務(wù)。
- 運(yùn)行的線程等于或多于corePoolSize,則將任務(wù)添加到隊(duì)列中。
- 當(dāng)任務(wù)隊(duì)列已滿,則在非corePool中創(chuàng)建新的線程來處理任務(wù)。
- 創(chuàng)建新線程將使當(dāng)前運(yùn)行的線程超出maximumPoolSize,任務(wù)將被拒絕,并調(diào)用RejectedExecutionHandler.rejectedExecution()方法。
線程池拒絕策略
線程池為我們提供了四種拒絕策略分別是:CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy
AbortPolicy
ThreadPoolExecutor中默認(rèn)的拒絕策略就是AbortPolicy直接拋出異常,具體實(shí)現(xiàn)如下
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()); } }
說明:這種策略非常簡(jiǎn)單粗暴,直接拋出RejectedExecutionException異常,也不會(huì)執(zhí)行后續(xù)的任務(wù)。
示例說明:
public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 10, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(1), new ThreadPoolExecutor.AbortPolicy()); //異步執(zhí)行 for(int i=0; i<10;i++) { System.out.println("添加第"+i+"個(gè)任務(wù)"); threadPoolExecutor.execute(new TestThread("線程"+i)); } } } public class TestThread implements Runnable { private String name; public TestThread(String name){ this.name=name; } @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread name:"+Thread.currentThread().getName()+",執(zhí)行:"+name); } }
執(zhí)行結(jié)果:
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.skywares.fw.juc.thread.TestThread@55f96302 rejected from java.util.concurrent.ThreadPoolExecutor@3d4eac69[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at com.skywares.fw.juc.thread.ThreadPoolTest.main(ThreadPoolTest.java:26)
thread name:pool-1-thread-5,執(zhí)行:線程5
thread name:pool-1-thread-2,執(zhí)行:線程1
thread name:pool-1-thread-4,執(zhí)行:線程4
thread name:pool-1-thread-3,執(zhí)行:線程3
thread name:pool-1-thread-1,執(zhí)行:線程0
thread name:pool-1-thread-5,執(zhí)行:線程2
從執(zhí)行結(jié)果我們得知,采用AbortPolicy策略當(dāng)任務(wù)執(zhí)行到第七個(gè)任務(wù)時(shí)會(huì)直接報(bào)錯(cuò),導(dǎo)致后續(xù)的業(yè)務(wù)邏輯不會(huì)執(zhí)行。
CallerRunsPolicy
CallerRunsPolicy在任務(wù)被拒絕添加后,會(huì)用調(diào)用execute函數(shù)的上層線程去執(zhí)行被拒絕的任務(wù)。
相關(guān)示例
public class ThreadPoolTest { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 10, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(1), new ThreadPoolExecutor.CallerRunsPolicy()); //異步執(zhí)行 for(int i=0; i<10;i++) { System.out.println("添加第"+i+"個(gè)任務(wù)"); threadPoolExecutor.execute(new TestThread("線程"+i)); } } }
執(zhí)行結(jié)果:
添加第0個(gè)任務(wù)
添加第1個(gè)任務(wù)
添加第2個(gè)任務(wù)
添加第3個(gè)任務(wù)
添加第4個(gè)任務(wù)
添加第5個(gè)任務(wù)
添加第6個(gè)任務(wù)
thread name:main,執(zhí)行:線程6
thread name:pool-1-thread-3,執(zhí)行:線程3
thread name:pool-1-thread-1,執(zhí)行:線程0
thread name:pool-1-thread-4,執(zhí)行:線程4
thread name:pool-1-thread-2,執(zhí)行:線程1
thread name:pool-1-thread-5,執(zhí)行:線程5
添加第7個(gè)任務(wù)
添加第8個(gè)任務(wù)
thread name:main,執(zhí)行:線程8
thread name:pool-1-thread-1,執(zhí)行:線程7
thread name:pool-1-thread-3,執(zhí)行:線程2
添加第9個(gè)任務(wù)
thread name:pool-1-thread-1,執(zhí)行:線程9
從執(zhí)行的結(jié)果我們可以得知,當(dāng)執(zhí)行到第7個(gè)任務(wù)時(shí),由于線程池拒絕策略,此任務(wù)由主線程來執(zhí)行,當(dāng)線程池有空閑時(shí),才繼續(xù)執(zhí)行其他的任務(wù)。所以此策略可能會(huì)阻塞主線程。
DiscardPolicy
這種拒絕策略比較簡(jiǎn)單,線程池拒絕的任務(wù)直接拋棄,不會(huì)拋異常也不會(huì)執(zhí)行
示例
修改上述的代碼,將拒絕策略修改為DiscardPolicy
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 10, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(1), new ThreadPoolExecutor.CallerRunsPolicy());
執(zhí)行結(jié)果
invoke dealStock success
goodsId:手機(jī)
thread name:pool-1-thread-1,執(zhí)行:線程0
thread name:pool-1-thread-4,執(zhí)行:線程4
thread name:pool-1-thread-5,執(zhí)行:線程5
thread name:pool-1-thread-3,執(zhí)行:線程3
thread name:pool-1-thread-2,執(zhí)行:線程1
thread name:pool-1-thread-1,執(zhí)行:線程2
從執(zhí)行的結(jié)果來看只執(zhí)行了6個(gè)任務(wù),其他的任務(wù)都被拋棄了。
DiscardOldestPolicy
DiscardOldestPolicy 當(dāng)任務(wù)拒絕添加時(shí),會(huì)拋棄任務(wù)隊(duì)列中最先加入隊(duì)列的任務(wù),再把新任務(wù)添加進(jìn)去。
示例說明
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 10, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(2), new ThreadPoolExecutor.CallerRunsPolicy());
執(zhí)行結(jié)果:
添加第0個(gè)任務(wù)
添加第1個(gè)任務(wù)
添加第2個(gè)任務(wù)
添加第3個(gè)任務(wù)
添加第4個(gè)任務(wù)
添加第5個(gè)任務(wù)
invoke dealStock success
goodsId:手機(jī)
thread name:pool-1-thread-2,執(zhí)行:線程3
thread name:pool-1-thread-1,執(zhí)行:線程0
thread name:pool-1-thread-1,執(zhí)行:線程2
thread name:pool-1-thread-2,執(zhí)行:線程1
自定義拒絕策略
當(dāng)線程池提供的拒絕策略無法滿足要求時(shí),我們可以采用自定義的拒絕策略,只需要實(shí)現(xiàn)RejectedExecutionHandler接口即可
public class CustRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { new Thread(r,"線程:"+new Random().nextInt(10)).start(); } } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 10, TimeUnit.MICROSECONDS, new LinkedBlockingDeque<>(2), new CustRejectedExecutionHandler());
執(zhí)行結(jié)果:
thread name:客戶線程:6,執(zhí)行:線程5
thread name:pool-1-thread-1,執(zhí)行:線程0
thread name:客戶線程:8,執(zhí)行:線程4
thread name:pool-1-thread-2,執(zhí)行:線程3
thread name:pool-1-thread-1,執(zhí)行:線程1
thread name:pool-1-thread-2,執(zhí)行:線程2
從執(zhí)行的結(jié)果來看,被拒絕的任務(wù)都在客戶的新線程中執(zhí)行。
小結(jié)
- AbortPolicy:直接拋出異常,后續(xù)的任務(wù)不會(huì)執(zhí)行
- CallerRunsPolicy:子任務(wù)執(zhí)行的時(shí)間過長,可能會(huì)阻塞主線程。
- DiscardPolicy:不拋異常,任務(wù)直接丟棄
- DiscardOldestPolicy;丟棄最先加入隊(duì)列的任務(wù)
總結(jié)
本文對(duì)于線程的池的幾種策略進(jìn)行詳細(xì)的講解,在實(shí)際的生產(chǎn)中需要集合相關(guān)的場(chǎng)景來選擇合適的拒絕策略,如有疑問,請(qǐng)隨時(shí)反饋。
到此這篇關(guān)于Java ThreadPoolExecutor的拒絕策略的文章就介紹到這了,更多相關(guān)Java ThreadPoolExecutor拒絕策略內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)Twitter的分布式自增ID算法snowflake
這篇文章主要介紹了Java實(shí)現(xiàn)Twitter的分布式自增ID算法snowflake,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Springboot實(shí)現(xiàn)對(duì)配置文件中的明文密碼加密詳解
我們?cè)赟pringBoot項(xiàng)目當(dāng)中,會(huì)把數(shù)據(jù)庫的用戶名密碼等配置直接放在yaml或者properties文件中,這樣維護(hù)數(shù)據(jù)庫的密碼等敏感信息顯然是有一定風(fēng)險(xiǎn)的。所以本文為大家整理了對(duì)配置文件中的明文密碼加密的方法,希望對(duì)大家有所幫助2023-03-03java微信企業(yè)號(hào)開發(fā)之發(fā)送消息(文本、圖片、語音)
這篇文章主要為大家詳細(xì)介紹了java微信企業(yè)號(hào)開發(fā)之發(fā)送消息,發(fā)送類型包括文本、圖片、語音,感興趣的小伙伴們可以參考一下2016-06-06JAVA中使用JSON進(jìn)行數(shù)據(jù)傳遞示例
本篇文章主要介紹了JAVA中使用JSON進(jìn)行數(shù)據(jù)傳遞示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01SpringBoot+Vue+Element-ui實(shí)現(xiàn)前后端分離
使用前后端分離的方式,可以減少代碼耦合,本文主要介紹了SpringBoot+Vue+Element-ui實(shí)現(xiàn)前后端分離,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06