BIO、NIO
BIO(Blocking I/O)
一、是什么?
BIO(阻塞I/O)是Java传统的同步阻塞I/O模型。当线程执行读写操作时,必须等待数据完全就绪(如客户端发送数据或连接建立),期间线程会被阻塞,无法执行其他任务。
二、解决什么问题
BIO适用于简单的客户端/服务器场景,如低并发应用。它通过多线程(每个连接一个线程)处理多个客户端请求,解决基础网络通信问题。
三、核心方法
ServerSocket.accept()
:阻塞等待客户端连接。InputStream.read()
:阻塞读取客户端数据。- 典型流程:主线程循环监听连接,为每个连接创建新线程处理I/O。
四、应用场景
- 低并发应用(如内部管理系统)。
- 客户端数量少且连接时间短的场景。
- 简单原型开发(代码简单易实现)。
五、Java示例
java
// 服务端代码
public class BioServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket clientSocket = serverSocket.accept(); // 阻塞等待连接
new Thread(() -> {
try
BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String request = reader.readLine(); // 阻塞读取数据
System.out.println("收到请求: " + request);
// 处理请求并响应
} catch (IOException e) { e.printStackTrace(); }
).start();
}
}
}
六、重要注意事项
- 线程开销大:每个连接需独立线程,消耗内存(默认1MB/线程),高并发时易崩溃。
- 资源浪费:线程阻塞时CPU闲置。
- 适用性:避免用于高并发场景(如即时通讯、游戏服务器)。
NIO(Non-blocking I/O)
一、是什么?
NIO(非阻塞I/O)是Java 1.4引入的异步I/O模型。核心是通道(Channel)、缓冲区(Buffer) 和选择器(Selector),线程通过选择器轮询多个通道的就绪状态,无需阻塞等待。
二、解决什么问题
解决BIO的高并发瓶颈:
- 单线程管理多连接:通过Selector轮询,一个线程处理数千连接。
- 零拷贝技术:减少数据在内存中的复制次数,提升传输效率。
- 避免线程阻塞:线程只在有就绪事件时工作,否则执行其他任务。
三、核心组件
- Channel:双向通信管道(如
ServerSocketChannel
)。 - Buffer:数据容器(如
ByteBuffer
)。 - Selector:监听多个Channel的事件(如连接、读写就绪)。
四、应用场景
- 高并发服务(如聊天服务器、API网关)。
- 需要长连接的应用(如WebSocket)。
- 大数据传输(零拷贝优化性能)。
五、Java示例
java
// 服务端代码
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(); // 阻塞直到有事件就绪
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
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);
buffer.flip();
System.out.println("收到数据: " + new String(buffer.array()));
}
iter.remove();
}
}
}
}
六、重要注意事项
- 编程复杂:需处理事件循环、缓冲区状态(flip/clear)。
- 空轮询问题:Linux下Selector可能因Bug无限循环,需用
epoll
替代。 - 适用性:适合高并发,但简单场景用BIO更快捷。
BIO vs NIO 区别
特性 | BIO | NIO |
---|---|---|
阻塞方式 | 同步阻塞(线程必须等待) | 同步非阻塞(线程轮询事件) |
线程模型 | 1连接 = 1线程 | 1线程处理多连接 |
并发能力 | 低(线程数限制) | 高(可支持数万连接) |
资源消耗 | 高(线程内存开销大) | 低(少量线程+事件驱动) |
适用场景 | 低并发、短连接 | 高并发、长连接 |
复杂度 | 简单(直同步代码) | 复杂(需理解Selector/Buffer) |
总结
- BIO:简单易用,适合低并发场景,但资源效率低。
- NIO:复杂但高效,适合高并发和长连接,需掌握事件驱动模型。
- 选择建议:
- 快速开发小应用 → BIO。
- 高性能服务器(如Netty底层)→ NIO。
- Java 7+推荐使用更简化的
NIO.2
(AIO)。