java线程池拒绝策略中,abortpolicy在队列满且线程数达最大时抛出rejectedexecutionexception,强制调用方处理;callerrunspolicy则让提交任务的线程同步执行该任务,起到反压作用。

Java线程池的拒绝策略,本质是“当系统实在忙不过来时,怎么体面地处理多出来的任务”。对初学者来说,最需要厘清的两个典型策略就是:AbortPolicy(抛异常)和CallerRunsPolicy(调用者执行)。它们代表了两种截然不同的应对哲学:一个是“立刻喊停”,一个是“自己先顶上”。
AbortPolicy:为什么一拒就抛异常?
这不是“甩锅”,而是明确传递一个信号:系统已过载。它在以下条件同时满足时触发:
- 线程池已达到最大线程数(
maximumPoolSize) - 工作队列也满了(比如
ArrayBlockingQueue(2)已塞满) - 线程池未关闭,但确实没地方放新任务了
这时,executor.execute(runnable) 不会静默失败,而是直接抛出 RejectedExecutionException。这个异常必须由调用方捕获——就像调用一个可能失败的数据库操作一样。初学者容易忽略的是:不加 try-catch,程序就会中断;加上之后,你才有机会记录日志、触发告警、降级处理,甚至把任务暂存到消息队列重试。
CallerRunsPolicy:谁提交,谁“加班”
这个策略不抛异常,也不丢任务,而是让“提交任务的那个线程”自己去执行它。比如你在主线程里调用 executor.execute(...),结果被拒绝了,那这段逻辑就会在主线程里同步运行。
- 它不是新开线程,也不是交给线程池,就是原地执行
- 效果上相当于给提交方“踩了一脚刹车”:主线程忙着执行任务,自然就慢下来,间接缓解了线程池压力
- 适合不能丢任务、又不想加复杂重试机制的场景,比如关键日志落盘、订单状态同步
注意:如果主线程本身是Web容器线程(如Tomcat的worker线程),让它执行耗时任务,会导致请求响应变慢,所以要评估任务执行时间是否可控。
两者对比的关键认知
初学者常混淆“谁在执行”和“谁在负责”。AbortPolicy 把责任交还给调用方——你要自己决定怎么兜底;CallerRunsPolicy 把执行权临时转嫁给调用方——你得承担它带来的阻塞风险。选哪个,取决于你的业务能不能接受丢任务、等延迟、或者崩流程。
动手验证的小建议
写个最小可运行例子,只配两个核心线程、队列容量为1、最大线程为2。提交4个带 Thread.sleep(2000) 的任务,就能稳定复现拒绝。分别换上 AbortPolicy 和 CallerRunsPolicy,观察控制台输出和线程名(用 Thread.currentThread().getName() 打印),一眼就能看出异常是否抛出、任务是否由 main 线程执行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











