硬核图解:从BIO到AIO,五种I/O模型一网打尽!
·
硬核图解:从BIO到AIO,五种I/O模型一网打尽!
|
🌺The Begin🌺点点关注,收藏不迷路🌺
|
1. 开篇:I/O模型是什么?为什么重要?
一次简单的数据读取,从应用程序发起请求到数据真正就绪,中间要经过操作系统内核、硬件中断、DMA等多层协作。不同的处理方式,就形成了不同的I/O模型。
I/O模型的核心区别在于两个阶段:
五大I/O模型:
- 阻塞I/O(Blocking I/O)- BIO
- 非阻塞I/O(Non-blocking I/O)- NIO
- I/O多路复用(I/O Multiplexing)- epoll/select
- 信号驱动I/O(Signal-driven I/O)
- 异步I/O(Asynchronous I/O)- AIO
2. 快速概览:五大I/O模型对比
| I/O模型 | 第一阶段(等待数据) | 第二阶段(拷贝到用户空间) | 整体是否阻塞 | 代表作 |
|---|---|---|---|---|
| 阻塞I/O | 阻塞 | 阻塞 | 全程阻塞 | 传统Socket |
| 非阻塞I/O | 轮询(不阻塞) | 阻塞 | 部分阻塞 | 设置NONBLOCK |
| I/O多路复用 | 阻塞(select/poll) | 阻塞 | 部分阻塞 | Nginx/Redis/Netty |
| 信号驱动I/O | 不阻塞(信号通知) | 阻塞 | 部分阻塞 | SIGIO信号 |
| 异步I/O | 不阻塞 | 不阻塞 | 全程不阻塞 | Windows IOCP/Linux io_uring |
3. 模型一:阻塞I/O(BIO)
3.1 核心流程
3.2 特点
3.3 Java代码示例
// 传统BIO服务端 - 每个连接一个线程
public class BioServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
// 此处阻塞,等待客户端连接
Socket socket = serverSocket.accept();
// 为每个连接创建新线程
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
String line;
// 此处阻塞,等待数据读取
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
4. 模型二:非阻塞I/O(NIO)
4.1 核心流程
4.2 非阻塞I/O流程图
4.3 特点
5. 模型三:I/O多路复用
5.1 核心流程
5.2 多路复用核心思想
5.3 select/poll/epoll 演进
5.4 多路复用工作流程
渲染错误: Mermaid 渲染失败: Parse error on line 3: ...LOOP{selector.select()} LOOP -->|有就绪 -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
5.5 Java NIO代码示例
// NIO多路复用服务端
public class NioServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 阻塞,等待就绪事件
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer); // 数据已就绪,不阻塞
// 处理数据...
}
}
}
}
}
6. 模型四:信号驱动I/O
6.1 核心流程
6.2 特点
7. 模型五:异步I/O(AIO)
7.1 核心流程
7.2 异步I/O vs 其他模型
7.3 Java AIO示例
// Java AIO 异步I/O
public class AioServer {
public static void main(String[] args) throws IOException {
AsynchronousServerSocketChannel serverChannel =
AsynchronousServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.accept(null, new CompletionHandler<>() {
@Override
public void completed(AsynchronousSocketChannel client, Object attachment) {
// 继续接受下一个连接
serverChannel.accept(null, this);
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 异步读取,不阻塞
client.read(buffer, buffer, new CompletionHandler<>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
// 数据已就绪,可直接使用
System.out.println("收到数据: " +
new String(attachment.array()));
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
exc.printStackTrace();
}
});
// 主线程不阻塞,可以继续其他工作
Thread.sleep(Integer.MAX_VALUE);
}
}
8. 五大模型对比总结
总结表
| I/O模型 | 等待数据阶段 | 拷贝数据阶段 | 系统调用次数 | 适用场景 |
|---|---|---|---|---|
| 阻塞I/O | 阻塞 | 阻塞 | 1次 | 低并发,简单场景 |
| 非阻塞I/O | 不阻塞(轮询) | 阻塞 | N次+1次 | CPU充足,连接数中等 |
| 多路复用 | 阻塞(select) | 阻塞 | 2次 | 高并发,如Nginx/Netty |
| 信号驱动 | 不阻塞 | 阻塞 | 2次 | 实际使用较少 |
| 异步I/O | 不阻塞 | 不阻塞 | 1次 | 高并发+高性能+高复杂度 |
9. 实战选型:我该用哪个?
业界实践
| 中间件/框架 | 使用的I/O模型 | 原因 |
|---|---|---|
| Nginx | 多路复用(epoll) | 高并发、可扩展性强 |
| Redis | 多路复用(epoll) | 单线程+事件驱动 |
| Netty | 多路复用 | Java生态最强NIO框架 |
| Tomcat 7+ | 多路复用(NIO) | 传统BIO → NIO演进 |
| Node.js | 多路复用(libuv) | 事件循环+异步 |
| Apache(Prefork) | BIO | 每请求一进程 |
10. 进阶:io_uring——Linux的新一代异步I/O
渲染错误: Mermaid 渲染失败: Lexical error on line 2. Unrecognized text. ...R subgraph 传统AIO(POSIX) P1[接 ----------------------^
io_uring 工作流程:
11. 面试高频追问
Q1:Java的NIO是NIO(非阻塞)还是多路复用?
答:Java NIO全称是New I/O,核心是多路复用模型,内部封装了select/poll/epoll,提供了非阻塞能力,本质是同步非阻塞 + 多路复用。
Q2:为什么说Netty高性能?
答:
- 使用I/O多路复用(epoll)
- 零拷贝技术
- 无锁化设计(Pipeline)
- 内存池(PooledByteBufAllocator)
Q3:select的1024限制是什么?
答:select使用fd_set结构,默认最大文件描述符数量为FD_SETSIZE=1024,是编译时常量。epoll没有此限制。
Q4:阻塞和非阻塞的区别是什么?
- 阻塞:调用结果返回前,当前线程被挂起
- 非阻塞:立即返回,不等待结果
12. 总结脑图
13. 一句话总结
五种I/O模型的本质区别在于“数据就绪的等待阶段”和“数据拷贝阶段”是否阻塞:BIO全程阻塞,NIO和多路复用拷贝阶段阻塞,信号驱动等待阶段不阻塞,只有AIO全程非阻塞。高并发场景下,多路复用(epoll)是目前最成熟的主流方案,AIO(io_uring)是未来的方向。
记忆口诀:
BIO傻等数据来,NIO轮询问内核,多路复用管家看,信号驱动等通知,AIO搞定全自动。
📌 如果你觉得这篇文章讲清楚了,欢迎点赞、收藏、评论交流!

|
🌺The End🌺点点关注,收藏不迷路🌺
|
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)