线程池拒绝策略主要包含5种,如下图所示,下面重点来详解线程池拒绝策略@mikechen
第一种:AbortPolicy(默认策略)
当线程池已满且无法继续接受新任务时,会抛出 RejectedExecutionException
异常,拒绝新任务的提交。
如下所示:
ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.AbortPolicy()); // Submit tasks executor.submit(() -> System.out.println("Task 1")); executor.submit(() -> System.out.println("Task 2")); // Will be rejected
这意味着新提交的任务会被丢弃,并且会通知调用者,告知线程池已经达到了最大负荷,无法继续接受任务。
备注:AbortPolicy 是线程池的默认拒绝策略。
第二种:CallerRunsPolicy
当线程池已满且无法继续接受新任务时,使用 CallerRunsPolicy 策略会尝试使用提交任务的线程来执行这个任务,而不是立即拒绝任务。
如下所示:
ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.CallerRunsPolicy()); // Submit tasks executor.submit(() -> System.out.println("Task 1")); executor.submit(() -> { System.out.println("Task 2 (CallerRunsPolicy)"); System.out.println("Executed by: " + Thread.currentThread().getName()); });
这种策略可能会影响提交任务的线程,因为它们需要执行额外的任务。
如果提交线程本身已经很繁忙,或者它们是在一个高并发的环境中被不断触发的,那么采用 CallerRunsPolicy 可能会导致提交线程的性能下降。
第三种:DiscardPolicy
当线程池已满时,直接丢弃新提交的任务,不会有任何异常抛出,这可能会导致一些任务被忽略。
如下所示:
ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy()); // Submit tasks executor.submit(() -> System.out.println("Task 1")); executor.submit(() -> System.out.println("Task 2")); // Will be discarded
DiscardPolicy 策略会直接丢弃新提交的任务,而不会抛出异常或执行任务。
这种策略适用于一些弱实时性要求的场景,其中丢失一部分任务不会对系统产生严重影响。
比如:日志记录、统计数据等任务可能在高峰期丢失一些数据,但不会对系统的核心功能造成影响。
第四种:DiscardOldestPolicy
当线程池已满时,丢弃工作队列中最早的任务,然后尝试将新提交的任务加入队列。
如下所示:
ThreadPoolExecutor executor = new ThreadPoolExecutor 1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.DiscardOldestPolicy()); // Submit tasks executor.submit(() -> System.out.println("Task 1")); executor.submit(() -> System.out.println("Task 2")); // Task 1 will be discarded
DiscardOldestPolicy 策略的目标是保留最新的任务,这在一些场景中可能是有用的。
比如:如果旧任务的执行结果已经不再重要,而最新的任务更具优先级,那么可以考虑使用这个策略。
第五种:自定义策略
你也可以根据应用需求实现自定义的拒绝策略,为此,你需要实现 RejectedExecutionHandler
接口。
如下所示:
class CustomRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("Custom Rejected Execution: " + r.toString()); } } ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new CustomRejectedExecutionHandler()); // Submit tasks executor.submit(() -> System.out.println("Task 1")); executor.submit(() -> System.out.println("Task 2")); // Will trigger custom rejection handler
这些线程池拒绝策略提供了不同的处理方式,可以根据你的应用场景和需求来选择合适的策略。
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》