new Thread() 高并发下易 OOM,因每线程默认占 1MB 栈内存且 OS 线程资源受限(如 Linux 默认 ulimit -u≈1024);ThreadPoolExecutor 的 corePoolSize 和 maxPoolSize 应依队列压力与弹性需求协同设定,非简单按 CPU 核数配置。

为什么 new Thread() 在高并发下会直接 OOM?
因为每次 new Thread() 都会申请独立的栈内存(默认 1MB),线程数一多,堆外内存直接爆。JVM 不回收线程栈,哪怕线程已 run 完——得等 GC 回收线程对象,而栈空间释放更滞后。
- 典型错误现象:
java.lang.OutOfMemoryError: unable to create new native thread - 不是堆内存不够,是操作系统级线程资源耗尽(Linux 默认
ulimit -u限制约 1024) - 即使业务逻辑极轻量(比如只 sleep 10ms),开 2000 个
Thread也大概率崩
ThreadPoolExecutor 的 corePoolSize 和 maxPoolSize 到底怎么设?
这两个参数不是“越大越好”,也不是“看 CPU 核数硬配”。它们共同决定线程池的弹性行为和队列压力传导路径。
-
corePoolSize:池中常驻线程数,即使空闲也不会被回收(除非allowCoreThreadTimeOut=true) -
maxPoolSize:仅当任务队列满且当前线程数 maxPoolSize 时,才新建线程;否则触发拒绝策略 - 常见误配:把
corePoolSize设为 0 → 线程全靠队列撑着,突发流量来时全部排队,响应延迟飙升 - I/O 密集型任务(如 HTTP 调用、DB 查询)可设为
2 * CPU核心数起步;CPU 密集型建议 ≤CPU核心数 + 1
用 Executors.newFixedThreadPool(10) 为什么线上出问题?
它底层是 new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<runnable>())</runnable> —— 问题全在那个无界队列 LinkedBlockingQueue 上。
- 所有任务都会进队列,线程数永远卡死在 10,再多请求也只排队不拒接
- 内存泄漏风险:队列持续积压,
LinkedBlockingQueue默认容量是Integer.MAX_VALUE,OOM 只是时间问题 - 正确做法:自己构造
ThreadPoolExecutor,用有界队列(如new ArrayBlockingQueue(100)),并配合理拒绝策略(如AbortPolicy或自定义)
submit() 和 execute() 抛异常的行为为什么不一样?
根本区别在于:前者返回 Future,后者不返回任何东西;而异常是否“可见”,取决于你有没有调用 Future.get()。
立即学习“Java免费学习笔记(深入)”;
-
execute(Runnable):如果任务本身抛异常,线程池会捕获并打印到System.err,但调用方完全感知不到 -
submit(Runnable)或submit(Callable):异常被包装进Future,必须显式调用future.get()才会 re-throw(且是ExecutionException包裹) - 容易踩的坑:用
submit()却从不get(),等于把异常吞掉,问题排查时完全找不到源头
线程池不是套个壳就完事,corePoolSize、workQueue、RejectedExecutionHandler 这三者得一起看——改一个不调另外两个,大概率埋雷。








