CallerRunsPolicy 背后的核心逻辑

🧠 详细解释:CallerRunsPolicy 背后的核心逻辑

我们来从线程池内部运行流程理解:

一、线程池正常执行流程(再强调一遍):

  1. 如果运行线程数 < corePoolSize:新建线程执行任务;

  2. 否则,任务入队列(如 ArrayBlockingQueue);

  3. 队列满后,如果线程数 < maxPoolSize:继续扩线程;

  4. 如果线程数也满了:触发 拒绝策略


二、CallerRunsPolicy 做了什么?

当线程池已满 + 队列已满时:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run(); // ✨ 关键:由提交者线程执行任务
    }
}

💡 理解重点:CallerRunsPolicy 本质是“阻塞式的限流器”

你提交任务的主线程(比如 main、Tomcat 工作线程、消息消费线程)会停下来执行这个任务。

  • 如果这个任务执行时间是 500ms,那提交线程会被卡住 500ms;

  • 这就起到了一种 “你提交太快了,我就拖慢你,让你慢慢交”。


🚧 举个例子:用来限流、缓冲请求

假设你有这样的线程池配置:

corePoolSize = 2  
maxPoolSize = 4  
queueCapacity = 10  
rejectedPolicy = CallerRunsPolicy

你疯狂提交 1000 万个任务,但线程池最多同时跑 4 个线程 + 缓存 10 个任务。

当执行慢 & 生产太快时:

  • 前几个任务 → 线程池接住了;

  • 任务越来越多 → 队列塞满、线程池满;

  • 第 15 个开始 → 主线程每提交一个任务,就得 自己执行一个任务

  • 每执行一个,才能继续提交下一个。

这就达到了:

🚦 主动限速 + ❄️ 保护线程池不崩溃 + 🧱 任务不丢 的效果。


🚫 如果不用 CallerRunsPolicy 会怎样?

换成 AbortPolicy:抛异常,任务直接丢失 ❌
换成 DiscardPolicy:啥也不管,任务悄悄丢了 ❌
换成 DiscardOldestPolicy:丢队列头的任务,也可能出严重错 ❌


✅ 所以:CallerRunsPolicy 是牺牲吞吐,换取稳定

它不适合处理极高并发的实时场景,但非常适合以下业务:

场景是否推荐使用 CallerRunsPolicy
日志异步落盘✅ 是,允许延迟但不能丢日志
消息异步消费(允许慢)✅ 是
Web 请求高并发写入(需高吞吐)❌ 否,会卡主线程,需限流+降级
数据分析离线任务✅ 是

🧠 延伸建议:面对 1000 万任务该怎么做?

如果你真有 “亿级任务量”,应该结合以下方案:

方案描述
✅ 分批提交控制每次提交任务的数量
✅ 异步分发 + 限流器比如 RateLimiter、漏桶
✅ 多线程池隔离拆分不同业务、避免阻塞
✅ 高性能 MQ 缓冲用 RocketMQ/Kafka 缓冲任务流
✅ 动态线程池可根据负载自动调节 poolSize
✅ 分布式调度平台如 XXL-Job、Quartz

📌 总结一句话

CallerRunsPolicy 是一种线程池压力过大时的“自我保护限流机制”。它不会丢任务,但会强迫提交线程“慢下来”,让提交者承担执行任务的代价,以此来缓冲资源压力、防止崩溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值