Java IO 从入门到深入(第六篇):BIO、NIO、AIO 全面对比(高并发核心 + 面试重点)
在前几篇中,我们已经学习了:
- 字节流 / 字符流
- 缓冲流
- 转换流
- 对象流
但这些都属于:
"如何读写数据"
而本篇我们要解决的是:
"如何高效处理大量连接和请求?"
这就涉及三种 IO 模型:
id="i1o2p3"
BIO(Blocking IO)
NIO(Non-Blocking IO)
AIO(Asynchronous IO)
一、什么是 IO 模型?
IO 模型本质是:
程序如何与操作系统进行数据交互
核心问题:
id="z1x2c3"
数据什么时候准备好?
线程是否需要等待?
如何处理多个连接?
二、BIO(阻塞 IO)
1 什么是 BIO?
BIO:
id="a1b2c3"
Blocking IO(阻塞 IO)
特点:
一个连接对应一个线程
2 工作流程
id="d4e5f6"
客户端连接 → 创建线程 → 阻塞等待数据 → 处理请求
3 示例代码(经典)
java
ServerSocket server = new ServerSocket(8080);
while(true){
Socket socket = server.accept(); // 阻塞
new Thread(() -> {
try{
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = is.read(buffer); // 阻塞
System.out.println(new String(buffer,0,len));
}catch(Exception e){
e.printStackTrace();
}
}).start();
}
4 BIO 特点总结
id="k1l2m3"
阻塞
一连接一线程
实现简单
5 缺点(重点)
id="n4o5p6"
线程开销大
无法支撑高并发
容易线程耗尽
三、NIO(非阻塞 IO)
1 什么是 NIO?
NIO:
id="q1w2e3"
Non-Blocking IO
特点:
一个线程可以处理多个连接
2 核心组件
id="r4t5y6"
Channel(通道)
Buffer(缓冲区)
Selector(选择器)
3 工作模型
id="u7i8o9"
一个线程
↓
Selector 监听多个连接
↓
事件触发(读 / 写)
↓
处理数据
4 示例(简化理解)
java
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while(true){
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
for(SelectionKey key : keys){
if(key.isAcceptable()){
// 处理连接
}
if(key.isReadable()){
// 处理读事件
}
}
}
5 NIO 核心特点
id="v7b6n5"
非阻塞
多路复用
单线程处理多连接
6 优点
id="m4n3b2"
高并发
资源利用率高
减少线程数量
7 缺点
id="l1k2j3"
编程复杂
理解难度高
四、AIO(异步 IO)
1 什么是 AIO?
AIO:
id="p1o2i3"
Asynchronous IO(异步 IO)
特点:
IO 完成后由系统通知程序
2 工作流程
id="u1y2t3"
发起请求 → 立即返回 → IO完成 → 回调通知
3 示例(简化)
java
AsynchronousServerSocketChannel server =
AsynchronousServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.accept(null, new CompletionHandler<>() {
@Override
public void completed(AsynchronousSocketChannel channel, Object attachment) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer, buffer, new CompletionHandler<>() {
@Override
public void completed(Integer result, ByteBuffer buf) {
System.out.println("读取完成");
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {}
});
}
@Override
public void failed(Throwable exc, Object attachment) {}
});
4 AIO 特点
id="x1c2v3"
真正异步
基于回调
无需阻塞线程
5 优缺点
优点:
id="b1n2m3"
性能更高
无需轮询
缺点:
id="q1w2e3"
实现复杂
使用较少
生态不成熟
五、BIO / NIO / AIO 对比(面试重点)
| 对比 | BIO | NIO | AIO |
|---|---|---|---|
| 模型 | 阻塞 | 非阻塞 | 异步 |
| 线程 | 一连接一线程 | 少量线程 | 更少 |
| 并发能力 | 低 | 高 | 更高 |
| 编程难度 | 简单 | 较复杂 | 很复杂 |
| 使用场景 | 小系统 | 高并发 | 超高并发 |
六、三者本质区别(面试必问)
核心问题:
线程是否等待 IO?
| 模型 | 是否阻塞 | 谁通知 |
|---|---|---|
| BIO | 是 | 无 |
| NIO | 否(轮询) | 程序 |
| AIO | 否 | 操作系统 |
七、NIO 为什么更快?(重点)
原因:
id="z9x8c7"
多路复用(Selector)
本质:
id="v6b5n4"
一个线程监听多个连接
而不是:
id="m3n2b1"
一个线程一个连接
八、实际应用场景
BIO
id="j1k2l3"
小型系统
简单服务
NIO
id="h4g5f6"
Netty
Tomcat
Redis
👉 主流方案
AIO
id="r7t8y9"
高端服务器
特殊场景
(实际使用较少)
九、Netty 与 NIO(面试扩展)
很多面试会问:
Netty 是什么?
答案:
id="u8i7o6"
基于 NIO 的高性能网络框架
优点:
id="p5o4i3"
封装复杂 NIO
提供高性能
易用
十、常见易错点
1 NIO 不是完全不阻塞
id="a2s3d4"
select() 仍可能阻塞
2 AIO 不等于 NIO
id="f5g6h7"
一个是异步
一个是非阻塞
3 NIO 编码复杂
id="j8k9l0"
Buffer / Channel / Selector 容易混乱
十一、面试高频问题(重点)
1 BIO、NIO、AIO 区别?
标准答:
id="z1x2c3"
BIO:阻塞
NIO:非阻塞 + 多路复用
AIO:异步回调
2 NIO 核心组件?
id="q4w5e6"
Channel
Buffer
Selector
3 什么是多路复用?
id="r7t8y9"
一个线程管理多个连接
4 为什么 NIO 更适合高并发?
id="u1i2o3"
减少线程数量
减少上下文切换
5 Netty 为什么快?
id="p4o5i6"
基于 NIO
线程模型优化
零拷贝
十二、总结
本篇是 Java IO 的 最终总结篇。
三种 IO 模型
id="k1k2k3"
BIO:简单但性能低
NIO:主流高并发方案
AIO:理论最强但用得少
核心思想
id="m4m5m6"
阻塞 → 非阻塞 → 异步
实际建议
id="n7n8n9"
学习重点:NIO + Netty