阻塞是因为在拒绝策略中调用queue.put()时,队列已满(如ArrayBlockingQueue(1)在2个任务提交后已达容量上限),而put()是阻塞方法,会无限等待空位,导致调用线程(主线程)进入WAITING状态。
直接用一段可运行的代码就能验证:在自定义拒绝策略中调用 queue.put(),会让提交任务的主线程卡住(即进入阻塞态),而不是继续执行后续逻辑。
为什么会出现阻塞?
因为线程池的拒绝策略是在 addWorker() 失败后、且队列已满时触发的。此时若你在拒绝策略里调用 queue.put(),而队列容量已满(比如 ArrayBlockingQueue 且 remainingCapacity() == 0),那么 put() 就会一直等待有空位——也就是无限期阻塞当前线程(即调用 execute() 的主线程)。
极简复现代码
以下代码仅需 20 行左右,即可清晰看到主线程被卡住:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
import java.util.concurrent.*;
public class RejectBlockDemo {
public static void main(String[] args) throws InterruptedException {
// 容量为 1 的阻塞队列 + 核心/最大线程数均为 1
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, 1, 0, TimeUnit.SECONDS, queue,
new ThreadPoolExecutor.CallerRunsPolicy() {{
// 替换为自定义拒绝策略:直接 put
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
try {
System.out.println("→ 进入拒绝策略,准备 put...");
queue.put(r); // ⚠️ 此处阻塞!
System.out.println("→ put 成功");
} catch (InterruptedException ignored) {}
}
}}
);
// 提交 3 个任务:第 1 个立即执行,第 2 个进队列,第 3 个触发拒绝策略
pool.execute(() -> { sleep(1000); System.out.println("task-1"); });
pool.execute(() -> { sleep(1000); System.out.println("task-2"); });
System.out.println("准备提交 task-3...");
pool.execute(() -> { System.out.println("task-3"); }); // ← 卡在这里!
System.out.println("这句话永远不会打印");
pool.shutdown();
}
static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) {} }
}
关键现象与验证点
- 程序输出停在 “准备提交 task-3...” 后,不再打印下一行,也不打印 “→ 进入拒绝策略,准备 put...”
- 用 jstack 或 IDE 的 Debug 模式观察线程栈,会发现
main线程处于WAITING (on object monitor),堆栈包含ArrayBlockingQueue.put和rejectedExecution - 说明:不是策略没触发,而是
put()调用本身把main线程挂起了
怎么避免这种阻塞?
如果真想在拒绝时“尝试塞回队列”,应使用非阻塞方式:
- 用
queue.offer(r, timeout, unit)并处理返回false的情况 - 或改用无界队列(如
LinkedBlockingQueue,但注意 OOM 风险) - 更稳妥的做法是记录日志、降级、告警,而非强行重试入队

















