自定义JAVA线程池拒绝策略

本文探讨了在面临大量瞬时任务时,如何选择和定制Java线程池的拒绝策略。业务场景涉及模块A通过队列将数据传递给模块B,但模块B的消费速度有限。通过对AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy和DiscardPolicy四种策略的分析,发现CallerRunsPolicy会导致主线程被占用,影响线程池效率。为解决这一问题,作者通过自定义策略,利用阻塞队列的remainingCapacity接口,使主线程在队列满时等待,直至有空余容量时将任务提交给线程池,实现了更符合业务需求的解决方案。
摘要由CSDN通过智能技术生成

最近一直被队列的消费业务所困扰,先大致说下业务状况。

模块A产生数据通过队列传递给模块B处理,但是数据来自于定时任务,经常是瞬时上万条或者更多,而且模块B的消费有限速控制并且能力有限(消费业务使用的线程池),肯定需要时间消化。

那么带来的一个问题就是线程池的拒绝策略选哪种?

首先说下线程池的四种拒绝策略:

  1. AbortPolicy:直接抛出异常。
  2. CallerRunsPolicy:只用调用者所在线程来运行任务。
  3. DiscardOldestPolicy:丢弃队列里最老的一个任务,并执行当前任务。
  4. DiscardPolicy:不处理,丢弃掉。

最初直观感受就是不能丢消息,用2吧,主线程跟着做业务。

久了,发现一个问题,主线程(暂且这么说吧,准确说是启动线程池的线程)一旦运行任务,即使线程池里的线程跑完任务都不会再进任务,饿着呢。直到主线程跑完一次业务,才能继续消费,分配给线程池任务。

问题很明显,业务流程比较耗时,主线程被占住了,线程池的一旦干完活,啥都干不了,都等着主线程消费队列的数据给新任务呢。

忍不了,但是分析其他三个策略,AbortPolicy直接抛异常,抛了能咋样,还是不知道要干啥;DiscardOldestPolicy丢弃老任务,丢消息,否了;DiscardPolicy丢弃,肯定否了。

于是查看拒绝策略源码,发现拒绝策略的这几个类真是够简洁了(只有两个方法),统一实现RejectedExecutionHandler接口,实现rejectedExecution方法(代码几行……),还有自己的构造方法(空的)。

然后分析自己的业务需求,总结:线程池的阻塞队列满了后,主线程啥都不干,就等着阻塞队列不满的时候,把任务扔给线程池。

看源码发现阻塞队列正好有个remainingCapacity接口,看看字面意思就知道是啥意思了,不过本着咱们猿们严谨的态度,继续深入看接口源码(这里不带着看了,确认是队列空余个数)。然后主线程只要不断获取空余个数,是0就继续获取,直到不是0为止。代码如下:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     if (!e.isShutdown()) {
          while (e.getQueue().remainingCapacity() == 0);
          e.execute(r);
     }
 }

Runnable r :主线程

ThreadPoolExecutor e:线程池

建议先看看其他四种策略的实现。

原以为这个问题比较复杂,结果只用几行代码搞定,不知道有没有坑……

ps:如果有问题,请指正交流。如果有更好的方案,欢迎指导。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值