Java I/O演进主线是让有限线程高效处理大量连接,核心在于线程与数据就绪状态的协作:BIO阻塞等待、NIO轮询询问、多路复用由内核通知、AIO内核完成回调。

Java I/O 的演进主线,其实是围绕“如何让有限线程高效处理大量连接”展开的。核心不在读写本身,而在于线程与数据就绪状态之间的协作关系——是等、是问、还是被通知。
阻塞IO(BIO):一个连接配一个线程,简单但不可扩展
BIO 是最直观的模型:调用 accept() 等连接、read() 等数据,线程全程挂起。没有数据就卡住,不能干别的。这种模式下,1000 个并发连接就需要 1000 个线程,线程上下文切换开销大,内存占用高,容易触发 OOM 或线程调度瓶颈。
典型表现:
- ServerSocket.accept() 阻塞,直到有新连接到来
- InputStream.read() 阻塞,直到客户端发来数据或连接关闭
- 每个连接独占线程,无法复用
非阻塞IO(基础 NIO):轮询检查,不等但耗 CPU
NIO 的 Channel 默认设为非阻塞后,read/write/accept 都立即返回。没数据时返回 -1 或抛出 java.nio.channels.ClosedChannelException,应用需自己循环调用检查——这就是“轮询”。它解放了线程等待,却把判断压力转给了应用层。
立即学习“Java免费学习笔记(深入)”;
关键局限:
- 线程必须持续调用 read(),即使 99% 的时间无数据可读
- 大量无效系统调用(用户态/内核态切换),CPU 使用率飙升
- 没有事件驱动机制,无法感知“哪个通道就绪”,只能全量扫描
I/O 多路复用(NIO 主流实践):一个线程管多个连接,靠内核通知
这才是现代 Java 高并发服务的基石。它不是简单的“非阻塞”,而是由操作系统提供 select/poll/epoll 这类多路复用器,让单个线程能同时监听成百上千个 Channel 的读、写、连接就绪状态。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
工作逻辑清晰:
- 注册感兴趣的 Channel 和事件类型(如 OP_READ)到 Selector
- 调用 selector.select(),线程挂起——但只等“有事件发生”,不等具体数据
- 内核在某个 Channel 数据就绪时唤醒 selector,返回就绪的 SelectionKey 列表
- 业务线程只对真正就绪的 Channel 做 read/write,避免空转
epoll(Linux)相比 select/poll 更高效:基于红黑树 + 就绪链表,时间复杂度 O(1),不随监控数量增长而变慢。
AIO(异步IO):内核代劳,完成即回调
AIO 是真正的异步:调用 read() 后线程立刻返回,内核在数据从网卡收齐、拷贝到用户缓冲区后,主动通过回调或 Future 通知应用。整个过程无需轮询、无需 selector,也不需要业务线程参与数据搬运。
但现实约束明显:
- Linux 对 AIO 支持不完善,底层仍常退化为线程池模拟
- Java 的 java.nio.channels.AsynchronousSocketChannel 在生产中使用率远低于 NIO+Selector
- 回调嵌套深、调试难,编程模型比同步代码更复杂
目前主流框架(Netty、Tomcat NIO connector)均基于 I/O 多路复用构建,而非 AIO。
从 BIO 到 NIO 再到多路复用,本质是把“等待权”逐步交还给内核:从线程自己死等 → 自己瞎问 → 内核主动喊你。选型不看名词多酷,而看谁能在你的连接规模、延迟要求和运维成熟度之间取得最佳平衡。


















