Executors.newFixedThreadPool 的最大并发数严格等于指定线程数nThreads,因核心数=最大数=队列无界,不扩容不拒绝;需严格限流时应自定义ThreadPoolExecutor配有界队列与拒绝策略,或用Semaphore精准控并发。

使用 Executors.newFixedThreadPool 不能直接控制“最大并发数”,它创建的是**固定大小的线程池**,其核心线程数 = 最大线程数 = 指定的 nThreads,且线程数不会动态增减。所谓“最大并发”,实际就是这个固定线程数——同一时刻最多只有这么多任务真正并行执行(不考虑 I/O 阻塞等场景)。
理解 newFixedThreadPool 的行为
它等价于:new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>())</runnable>
- 核心线程数和最大线程数相同,都是
nThreads,线程永不销毁(空闲时也保留) - 任务队列是无界
LinkedBlockingQueue,新任务提交后,若所有线程都在忙,就排队等待,**不会触发拒绝策略,也不会新建线程** - 因此,“最大并发执行数”严格等于
nThreads;但“最大待处理任务数”理论上无限(受内存限制)
如何真正限制最大并发 + 合理拒绝超载
如果需要严格控制同时运行的任务数,并对超载任务做明确处理(比如快速失败),应避免用 newFixedThreadPool,改用自定义 ThreadPoolExecutor:
- 设置核心线程数 = 最大线程数 = 你期望的并发上限(如 5)
- 使用有界队列(如
ArrayBlockingQueue(10)),防止内存溢出 - 指定拒绝策略,例如
AbortPolicy(抛异常)、CallerRunsPolicy(由提交线程自己执行)
示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 5, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(10),
new ThreadPoolExecutor.AbortPolicy()
);
常见误区:以为 submit 多个任务就等于并发执行
即使线程池只有 2 个线程,你调用 100 次 submit(),也只会同时最多 2 个任务在跑,其余 98 个在队列里等——这不叫“并发”,只是“提交”。真正的并发度由活跃线程数决定,不是提交数。
立即学习“Java免费学习笔记(深入)”;
若想观察实际并发,可用 executor.getActiveCount() 查看当前正在执行任务的线程数。
替代方案:用 Semaphore 控制逻辑并发度
如果业务上需限制某段代码的并发访问(比如调用第三方接口限流),比线程池更轻量、更精准的方式是用 Semaphore:
- 初始化
new Semaphore(3)表示最多 3 个线程可同时进入临界区 - 每个任务执行前
semaphore.acquire(),结束后semaphore.release() - 配合线程池使用,能实现“N 线程池 + M 并发执行”的组合控制


















