每个Socket配一个线程在高并发下会崩,因线程栈内存开销大、上下文切换频繁、OS线程数限制(如1024)及文件描述符泄漏,导致OOM或“unable to create native thread”;阻塞I/O使线程闲置占坑,无法复用,ThreadPoolExecutor仅缓解表象,本质需改用NIO Selector模型。

为什么每个Socket配一个线程在高并发下会崩
Java BIO 的 ServerSocket.accept() 是阻塞的,每 accept 一个连接就 new 一个线程去处理 InputStream.read(),看似简单,但线程本身有开销:栈内存(默认1MB)、上下文切换、JVM线程调度压力。当并发连接到几千时,线程数暴涨,不是 CPU 先跑满,而是直接 OOM 或系统拒绝创建新线程(java.lang.OutOfMemoryError: unable to create native thread)。
常见错误现象包括:服务启动后前几十个连接正常,之后新连接卡住或超时;jstack 看到数百上千个 WAITING 状态的线程堆在 read() 上;GC 频繁但堆内存不高——说明压力在本地线程栈和 OS 资源上。
- 线程栈大小可通过
-Xss256k降,但治标不治本 - Linux 默认单进程线程数限制常为 1024,
ulimit -u可查,超了就抛异常 -
ThreadPoolExecutor做线程复用能缓解,但无法解决“阻塞 I/O 期间线程闲置”这个本质问题
Socket 输入流阻塞时线程根本没法干别的
BIO 的 InputStream.read() 在没数据时会一直挂起线程,哪怕只等一个字节。这意味着:1000 个空闲连接 = 1000 个被锁死的线程,它们既不处理业务,也不释放资源,纯属占坑。
典型使用场景如长连接心跳、客户端发包间隔不均、网络抖动导致分包延迟——这些都会让线程长时间停在 read() 上,而你完全没法干预或超时中断(除非用 setSoTimeout(),但它只能中断一次 read,且超时后仍要重试,逻辑变复杂)。
立即学习“Java免费学习笔记(深入)”;
-
socket.setSoTimeout(5000)可避免无限等待,但会抛SocketTimeoutException,需手动循环重试 - 没有数据时,线程无法被回收或复用,
ThreadPoolExecutor的allowCoreThreadTimeOut对阻塞中的线程无效 - 如果客户端只发半包或断连不通知,
read()可能永远不返回,线程永久泄漏
替代方案不是“优化线程池”,而是换模型
真要撑住高并发,就得放弃“一连接一线程”思路。NIO 的 Selector + 单线程轮询多个 SocketChannel 才是正解——一个线程管几千连接,只在有数据可读/可写时才触发回调。
如果你非得用 BIO(比如 legacy 系统不能改协议),唯一现实的折中是:用固定大小线程池 + 连接数限流 + 快速失败。别幻想靠调参扛住 C10K。
- 用
new ThreadPoolExecutor(50, 50, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(100))控制最大并发处理数 -
ServerSocket构造时传50作为 backlog,防连接洪峰堆积 - accept 后立即检查当前活跃线程数,超阈值就
socket.close()并返回拒绝响应(如 HTTP 503) - 别碰
Executors.newCachedThreadPool()——它会无节制新建线程,等于把问题外包给 JVM
容易被忽略的 Socket 资源泄漏点
线程崩得快,往往不是因为线程多,而是因为 Socket 没关干净。BIO 下每个 Socket 对应一个文件描述符(fd),Linux 系统级限制比线程更早触顶(ulimit -n 默认常为 1024)。
一旦某个线程因异常没走到 socket.close(),fd 就泄露;反复几次后,accept() 开始抛 IOException: Too many open files,这时连新连接都进不来,比线程耗尽还致命。
- 必须用 try-with-resources 包住
Socket和它的InputStream/OutputStream - 不要只在 try 块里 close——异常可能发生在 read 中间,finally 才是兜底位置
-
Socket.isClosed()和isConnected()都不可靠,关之前先判!socket.isClosed()再关 - 用
lsof -p <pid>定期检查 fd 数量,比等报错再查强得多
线程模型选错是设计问题,资源没关是实现问题——后者更容易在线上静默积累,直到某天突然雪崩。









