Java中有界隊列的飽和策略(reject policy)原理解析
我們在使用ExecutorService的時候知道,在ExecutorService中有個一個Queue來保存提交的任務(wù),通過不同的構(gòu)造函數(shù),我們可以創(chuàng)建無界的隊列(ExecutorService.newCachedThreadPool)和有界的隊列(ExecutorService newFixedThreadPool(int nThreads))。
無界隊列很好理解,我們可以無限制的向ExecutorService提交任務(wù)。那么對于有界隊列來說,如果隊列滿了該怎么處理呢?
今天我們要介紹一下java中ExecutorService的飽和策略(reject policy)。
以ExecutorService的具體實現(xiàn)ThreadPoolExecutor來說,它定義了4種飽和策略。分別是AbortPolicy,DiscardPolicy,DiscardOldestPolicy和CallerRunsPolicy。
如果要在ThreadPoolExecutor中設(shè)定飽和策略可以調(diào)用setRejectedExecutionHandler方法,如下所示:
ThreadPoolExecutor threadPoolExecutor= new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(20)); threadPoolExecutor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy() );
上面的例子中我們定義了一個初始5個,最大10個工作線程的Thread Pool,并且定義其中的Queue的容量是20。如果提交的任務(wù)超出了容量,則會使用AbortPolicy策略。
AbortPolicy
AbortPolicy意思是如果隊列滿了,最新的提交任務(wù)將會被拒絕,并拋出RejectedExecutionException異常:
public static class AbortPolicy implements RejectedExecutionHandler { /** * Creates an {@code AbortPolicy}. */ public AbortPolicy() { } /** * Always throws RejectedExecutionException. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task * @throws RejectedExecutionException always */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
上面的代碼中,rejectedExecution方法中我們直接拋出了RejectedExecutionException異常。
DiscardPolicy
DiscardPolicy將會悄悄的丟棄提交的任務(wù),而不報任何異常。
public static class DiscardPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardPolicy}. */ public DiscardPolicy() { } /** * Does nothing, which has the effect of discarding task r. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
DiscardOldestPolicy
DiscardOldestPolicy將會丟棄最老的任務(wù),保存最新插入的任務(wù)。
public static class DiscardOldestPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardOldestPolicy} for the given executor. */ public DiscardOldestPolicy() { } /** * Obtains and ignores the next task that the executor * would otherwise execute, if one is immediately available, * and then retries execution of task r, unless the executor * is shut down, in which case task r is instead discarded. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
我們看到在rejectedExecution方法中,poll了最老的一個任務(wù),然后使用ThreadPoolExecutor提交了一個最新的任務(wù)。
CallerRunsPolicy
CallerRunsPolicy和其他的幾個策略不同,它既不會拋棄任務(wù),也不會拋出異常,而是將任務(wù)回退給調(diào)用者,使用調(diào)用者的線程來執(zhí)行任務(wù),從而降低調(diào)用者的調(diào)用速度。我們看下是怎么實現(xiàn)的:
public static class CallerRunsPolicy implements RejectedExecutionHandler { /** * Creates a {@code CallerRunsPolicy}. */ public CallerRunsPolicy() { } /** * Executes task r in the caller's thread, unless the executor * has been shut down, in which case the task is discarded. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
在rejectedExecution方法中,直接調(diào)用了 r.run()方法,這會導(dǎo)致該方法直接在調(diào)用者的主線程中執(zhí)行,而不是在線程池中執(zhí)行。從而導(dǎo)致主線程在該任務(wù)執(zhí)行結(jié)束之前不能提交任何任務(wù)。從而有效的阻止了任務(wù)的提交。
使用Semaphore
如果我們并沒有定義飽和策略,那么有沒有什么方法來控制任務(wù)的提交速度呢?考慮下之前我們講到的Semaphore,我們可以指定一定的資源信號量來控制任務(wù)的提交,如下所示:
public class SemaphoreUsage { private final Executor executor; private final Semaphore semaphore; public SemaphoreUsage(Executor executor, int count) { this.executor = executor; this.semaphore = new Semaphore(count); } public void submitTask(final Runnable command) throws InterruptedException { semaphore.acquire(); try { executor.execute(() -> { try { command.run(); } finally { semaphore.release(); } } ); } catch (RejectedExecutionException e) { semaphore.release(); } } }
本文的例子可參考https://github.com/ddean2009/learn-java-concurrency/tree/master/rejectPolicy
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- java中用數(shù)組實現(xiàn)環(huán)形隊列的示例代碼
- Java 延遲隊列的常用的實現(xiàn)方式
- 詳解Java中的延時隊列 DelayQueue
- Java 1.8使用數(shù)組實現(xiàn)循環(huán)隊列
- Java特性隊列和棧的堵塞原理解析
- Java實現(xiàn)自定義阻塞隊列
- Java優(yōu)先隊列(PriorityQueue)重寫compare操作
- Java實現(xiàn)隊列的三種方法集合
- SpringBoot集成JmsTemplate(隊列模式和主題模式)及xml和JavaConfig配置詳解
- java隊列之queue用法實例分析
- JAVA 實現(xiàn)延遲隊列的方法
- 詳解java中的阻塞隊列
- 一口氣說出Java 6種延時隊列的實現(xiàn)方法(面試官也得服)
- Java阻塞隊列四組API介紹(小結(jié))
- Java消息隊列JMS實現(xiàn)原理解析
- java鏈表應(yīng)用--基于鏈表實現(xiàn)隊列詳解(尾指針操作)
- Java循環(huán)隊列原理與用法詳解
- 10分鐘搞定Java并發(fā)隊列
相關(guān)文章
springboot本地調(diào)試沒問題,打包運行報錯原因及分析
這篇文章主要介紹了springboot本地調(diào)試沒問題,打包運行報錯原因及分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05Struts2攔截器Interceptor的原理與配置實例詳解
攔截器是一種AOP(面向切面編程)思想的編程方式.它提供一種機制是開發(fā)者能夠把相對獨立的代碼抽離出來,配置到Action前后執(zhí)行。下面這篇文章主要給大家介紹了關(guān)于Struts2攔截器Interceptor的原理與配置的相關(guān)資料,需要的朋友可以參考下。2017-11-11