线程池“满了”分三阶段:核心线程满→队列满→最大线程数满;任务仅在核心线程全忙时才入队,队列类型决定排队行为,队列满后仍会尝试扩容至maximumPoolSize,仅当队列满且线程数已达上限才拒绝任务。

线程池“满了”不是单一状态,而是分阶段的:先核心线程满 → 再队列满 → 最后最大线程数也满。任务排队处理的关键,就卡在这三个环节的顺序和策略上。
任务排队发生在核心线程满之后
只要当前运行线程数 小于 corePoolSize,新任务会立刻创建核心线程执行,根本不会排队。只有当核心线程全部忙于执行任务时,后续任务才会被放入任务队列等待——这是排队的起点。
比如 corePoolSize=5,前 5 个任务各占一个线程;第 6 个任务不会新建第 6 个线程,而是进队列排队,哪怕 maximumPoolSize 设的是 20。
队列类型决定排队是否可控
不同队列行为差异极大:
立即学习“Java免费学习笔记(深入)”;
- ArrayBlockingQueue(有界):容量固定,例如设为 100。第 101 个任务进来时,若线程数还没到 maximumPoolSize,就触发扩容;否则走拒绝策略。
- LinkedBlockingQueue(无界,默认未指定容量):理论上可无限排队,但容易掩盖负载问题,最终可能引发 OOM。
- SynchronousQueue(直接提交):不存储任务,相当于“零排队”。任务必须立刻由空闲线程接收,否则立即尝试扩容或拒绝。
队列满后不是直接拒绝,而是尝试扩容
队列已满只是中间状态,线程池还会再判断一次:当前线程数是否小于 maximumPoolSize。
如果是,就创建非核心线程来执行这个新任务——此时任务并不排队,而是跳过队列、直接执行。
只有当队列满 且 线程数已达 maximumPoolSize 时,才真正触发拒绝策略(如抛异常、丢弃、调用方自己执行等)。
排队不是被动等待,可以主动干预
默认逻辑是“队列填满才扩容”,但业务对延迟敏感时,这种滞后扩容不合适。可行的优化方式包括:
- 重写队列的 offer() 方法,在使用率超过 70% 时主动触发 ensurePrestart() 提前扩容;
- 选用小容量有界队列 + 足够大的 corePoolSize,让多数任务直奔线程执行,减少排队概率;
- 配合 allowCoreThreadTimeOut(true) 和较短的 keepAliveTime,在低峰期回收核心线程,腾出资源应对突发流量。


















