BIO vs NIO vs AIO
面试提问
"BIO、NIO、AIO 有什么区别?各自适用什么场景?"
核心概念
Java IO 模型经历了三代演进:
| 模型 | 全称 | 特点 | 引入版本 |
|---|---|---|---|
| BIO | Blocking IO | 同步阻塞 | JDK 1.0 |
| NIO | Non-blocking IO | 同步非阻塞 | JDK 1.4 |
| AIO | Asynchronous IO | 异步非阻塞 | JDK 1.7 |
BIO(同步阻塞 IO)
工作模式
┌─────────────────────────────────────────────┐
│ BIO 模型 │
├─────────────────────────────────────────────┤
│ Thread-1 ──阻塞──> read() ──等待──> 返回 │
│ Thread-2 ──阻塞──> read() ──等待──> 返回 │
│ Thread-3 ──阻塞──> read() ──等待──> 返回 │
│ ... │
│ 每个连接需要一个线程 │
└─────────────────────────────────────────────┘示例代码
// BIO 服务端
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待连接
new Thread(() -> {
try {
InputStream is = socket.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) { // 阻塞读取
// 处理数据
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}特点
- 优点:模型简单,代码直观
- 缺点:
- 一个连接需要一个线程,资源消耗大
- 线程阻塞,CPU 利用率低
- 无法支撑高并发
适用场景
- 连接数少且固定
- 服务端资源充足
- 简单的请求响应模型
NIO(同步非阻塞 IO)
工作模式
┌─────────────────────────────────────────────┐
│ NIO 模型 │
├─────────────────────────────────────────────┤
│ Selector(多路复用器) │
│ ↓ 轮询 │
│ Channel-1 (可读) ──> 处理 │
│ Channel-2 (可写) ──> 处理 │
│ Channel-3 (无事件) ──> 跳过 │
│ ... │
│ 一个线程管理多个连接 │
└─────────────────────────────────────────────┘三大核心组件
| 组件 | 作用 | 类比 |
|---|---|---|
| Channel | 双向通道,可读可写 | 铁轨 |
| Buffer | 数据容器,读写都经过它 | 火车车厢 |
| Selector | 多路复用器,监控 Channel 事件 | 调度员 |
示例代码
// NIO 服务端
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false); // 非阻塞模式
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册接受连接事件
while (true) {
selector.select(); // 阻塞直到有事件发生
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.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);
// 处理数据
}
}
}特点
- 优点:
- 一个线程管理多个连接
- 非阻塞,CPU 利用率高
- 支持高并发
- 缺点:模型复杂,编程难度高
适用场景
- 连接数多且连接时间短
- 高并发场景
- Netty、Tomcat NIO 等框架
AIO(异步非阻塞 IO)
工作模式
┌─────────────────────────────────────────────┐
│ AIO 模型 │
├─────────────────────────────────────────────┤
│ 应用程序发起 read() 请求 │
│ ↓ 立即返回,不阻塞 │
│ 操作系统完成读取 │
│ ↓ 回调 │
│ CompletionHandler 处理结果 │
└─────────────────────────────────────────────┘示例代码
// AIO 服务端
AsynchronousServerSocketChannel serverChannel =
AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel client, Void attachment) {
serverChannel.accept(null, this); // 接受下一个连接
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
// 处理数据
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
// 处理异常
}
});
}
@Override
public void failed(Throwable exc, Void attachment) {
// 处理异常
}
});特点
- 优点:
- 真正的异步,操作系统完成 IO
- 回调机制,编程相对简单
- 缺点:
- Linux 支持不完善(epoll 边缘触发)
- Windows 支持较好(IOCP)
适用场景
- 连接数多且连接时间长
- 需要真正异步的场景
- Windows 平台
三种模型对比
| 对比项 | BIO | NIO | AIO |
|---|---|---|---|
| 阻塞方式 | 同步阻塞 | 同步非阻塞 | 异步非阻塞 |
| 线程模型 | 一连接一线程 | 一线程多连接 | 回调模型 |
| 并发能力 | 低 | 高 | 高 |
| 编程难度 | 简单 | 复杂 | 中等 |
| 可靠性 | 差 | 好 | 好 |
| 吞吐量 | 低 | 高 | 高 |
| Linux 支持 | 好 | 好(epoll) | 一般 |
面试要点总结
| 问题 | 答案要点 |
|---|---|
| BIO 特点? | 同步阻塞,一连接一线程,简单但性能差 |
| NIO 特点? | 同步非阻塞,Selector 多路复用,高性能 |
| AIO 特点? | 异步非阻塞,操作系统完成 IO,回调机制 |
| NIO 为什么高效? | 一个线程管理多个连接,减少线程切换开销 |
| 如何选择? | 低并发用 BIO,高并发用 NIO,Windows 高并发可考虑 AIO |
相关题目
- Netty 为什么选择 NIO 而不是 AIO?
- Selector 是如何实现多路复用的?
- BIO 如何优化支持更多连接?
- 为什么 Linux 下 AIO 性能不如 NIO?
参考资料
- 《Java NIO》- Ron Hitchens
- Java NIO Tutorial (opens in a new tab)
- Netty 官方文档