一、3 大 I/O 模型真实的关系是什么?
3 大 I/O 模型
├─ BIO(同步阻塞)
│ └─ 通知方式:❌ 无通知(线程挂起等)
│
├─ NIO(同步非阻塞)= 多路复用
│ ├─ select/poll:⚠️ 轮询(O(n))
│ └─ epoll:⚠️ 事件驱动(O(1))
│
└─ AIO(异步非阻塞)
└─ ✅ 回调(OS 通知)
真相是:
- 3 大 I/O 模型 = BIO / NIO / AIO (按同步性 + 阻塞性分类)
- "轮询 / 事件驱动 / 回调" 是 3 种通知方式 (不是 3 大 I/O 模型)
- 每种 I/O 模型用不同的通知方式 :
- BIO :无通知 (线程挂起)
- NIO :轮询 (select/poll )或事件驱动 (epoll)
- AIO :回调 (OS 异步通知)
二、NIO 多路复用的核心组件
2.1 三大核心概念
┌─────────────────────────────────────────────────┐
│ Java NIO 多路复用模型 │
├─────────────────────────────────────────────────┤
│ │
│ Selector(多路复用器) │
│ ├─ Channel 1(FileChannel) │
│ ├─ Channel 2(SocketChannel) │
│ ├─ Channel 3(ServerSocketChannel) │
│ └─ Channel N(...) │
│ │
│ ⚠️ Selector 内部"轮询"这些 Channel │
│ 哪个有事件就处理哪个 │
│ │
└─────────────────────────────────────────────────┘
| 组件 | 作用 | 比喻 |
|---|---|---|
| Channel | 数据通道(双向) | 水管 |
| Buffer | 数据缓冲区 | 水桶 |
| Selector | 多路复用器 | 总开关(轮询所有水管) |
2.2 工作流程(核心 4 步)
// 1. 创建 Selector
Selector selector = Selector.open();
// 2. 把 Channel 注册到 Selector
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // ⚠️ 非阻塞
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
// 3. 轮询(**核心步骤**)
while (true) {
// ⚠️ selector.select() 内部轮询所有 Channel
int readyChannels = selector.select(); // 阻塞,直到有 Channel 就绪
if (readyChannels == 0) continue;
// 4. 处理就绪的 Channel
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
// 处理新连接
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
// 处理读事件
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
// ... 处理业务逻辑
}
iter.remove();
}
}
三、NIO 轮询 vs 软件系统轮询 (核心对比)
| 维度 | NIO 多路复用轮询 | 软件系统轮询 |
|---|---|---|
| 轮询对象 | OS 内核的 Channel 集合 | 服务器 / 数据库 |
| 轮询位置 | OS 内核(零拷贝) | 应用层(用户态) |
| 轮询方式 | select / poll / epoll | setInterval / @Scheduled |
| 阻塞 | 阻塞内核 (select() 阻塞) |
HTTP 阻塞 |
| 性能 | 极高 (单线程处理万级连接) | 一般 (每次都是完整 HTTP 请求) |
| 资源消耗 | 极小 (复用连接) | 大 (每次新建连接) |
| 实时性 | 毫秒级 | 秒级(取决于间隔) |
| 项目 | Netty / Spring Cloud Gateway | 5 秒轮询报表 |
四、NIO 轮询的 3 大底层实现 (重要)
4.1 select / poll / epoll 对比
| 维度 | select | poll | epoll (Linux) |
|---|---|---|---|
| 时间复杂度 | O(n) | O(n) | O(1) |
| 文件描述符限制 | 1024 | 无限制 | 无限制 |
| 内核实现 | 轮询 | 轮询 | 事件驱动 (回调) |
| 跨平台 | ✅ | ✅ | ❌(仅 Linux) |
| 性能 | 差 | 差 | ⚠️⚠️⚠️ 极好 |
| 老哥项目 | 老项目 | 老项目 | Netty / Spring Cloud Gateway |
4.2 select 轮询机制 (最差,但跨平台)
// select 实现:内核轮询所有 fd
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
报表定时任务轮询 ------应用层 select 类似的 setInterval。
4.3 epoll 事件驱动机制 (最好,Linux)
// epoll 实现:内核用红黑树 + 事件回调
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
Spring Cloud Gateway (WebFlux 异步非阻塞 )------底层用 epoll。
五、NIO 多路复用的 5 大优势
5.1 单线程处理万级连接
Spring Cloud Gateway 实战 (Netty 底层):
1 个 Netty 线程 → 处理 1w+ 并发连接
vs
1 个 Tomcat 线程 → 处理 200 并发连接
5.2 零拷贝 (mmap / sendfile)
传统 I/O:磁盘 → 内核缓冲 → 用户缓冲 → Socket 缓冲 → 网卡
NIO 零拷贝:磁盘 → 内核缓冲 → 网卡(少 2 次拷贝)
5.3 事件驱动 (不轮询)
select/poll:内核**轮询**所有 fd
epoll:内核用**回调**通知(**事件驱动**)
5.4 内存映射 (mmap)
// MappedByteBuffer:直接把文件映射到内存
FileChannel channel = FileChannel.open(Paths.get("large-file.log"));
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_ONLY, 0, channel.size());
// 读取就像读内存一样快
5.5 Channel 双向 (比 Stream 强)
| 维度 | Stream(BIO) | Channel(NIO) |
|---|---|---|
| 读写 | 单向 | 双向 |
| 缓冲 | ❌ | 必须配合 Buffer |
| 阻塞 | ❌ | 非阻塞 |
| 多路复用 | ❌ | ✅ |
六、3 大 I/O 模型类比
"BIO = 线程挂起等 (像打电话,对方不接就一直等 ) NIO = 主动轮询 / 事件通知 (像发短信,发了就干别的,等回信再看 ) AIO = OS 回调(像发短信 + 对方主动打电话给你)"
七、记忆口诀
"3 大 I/O 模型:BIO 同步阻塞,NIO 同步非阻塞,AIO 异步非阻塞"
"3 种通知方式:轮询 / 事件驱动 / 回调" (不是 3 大 I/O 模型)
"BIO = 线程挂起等(不是中断)"
"NIO = 多路复用(select/poll 轮询,epoll 事件驱动)"
"AIO = OS 回调"
"Spring Cloud Gateway / Redis / Kafka 都用 NIO"
八、总结
1. 什么是轮询?
└─ 软件系统层面:客户端反复问服务器(应用层)
2. NIO 多路复用中的轮询?
└─ OS 层面:NIO 底层 select/poll 是真轮询,epoll 是事件驱动
3. 3 大 I/O 模型?
└─ BIO / NIO / AIO(按同步性 + 阻塞性分类)
└─ 3 种通知方式:轮询 / 事件驱动 / 回调(不是 3 大 I/O 模型)
4. BIO 是中断吗?NIO 是事件驱动?AIO 是回调?
└─ ❌ BIO 不是中断(是线程挂起)
└─ ⚠️ NIO 不全是事件驱动(select/poll 是轮询,epoll 才是)
└─ ✅ AIO 是回调