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