Java IO 与 NIO 比较
·
1. 基本概念对比
| 特性 | Java IO (OIO/BIO) | Java NIO |
|---|---|---|
| 全称 | Blocking IO | New IO / Non-blocking IO |
| 核心方向 | 面向流(Stream) | 面向缓冲区(Buffer) |
| 阻塞性 | 阻塞 | 非阻塞 |
| 通道 | 单向 | 双向(Channel) |
2. 详细区别
2.1 面向流 vs 面向缓冲区
// IO - 面向流
InputStream in = new FileInputStream("file.txt");
int data = in.read(); // 直接从流读取字节
// NIO - 面向缓冲区
FileChannel channel = FileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer); // 读取到缓冲区
buffer.flip(); // 切换读模式
2.2 阻塞 vs 非阻塞
// IO - 阻塞模式
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket = serverSocket.accept(); // 阻塞直到连接
InputStream in = socket.getInputStream();
in.read(); // 阻塞直到有数据
// NIO - 非阻塞模式
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false); // 设置为非阻塞
channel.bind(new InetSocketAddress(8080));
SocketChannel client = channel.accept(); // 立即返回,可能为null
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
3. 核心组件
3.1 IO 核心组件
-
InputStream/OutputStream: 字节流
-
Reader/Writer: 字符流
-
File: 文件操作
-
Socket: 网络通信
3.2 NIO 核心组件
// 1. Buffer - 缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("hello".getBytes());
buffer.flip(); // 切换读模式
while(buffer.hasRemaining()) {
System.out.print((char)buffer.get());
}
// 2. Channel - 通道
FileChannel fileChannel = FileChannel.open(path);
SocketChannel socketChannel = SocketChannel.open();
// 3. Selector - 选择器(多路复用)
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
while(true) {
selector.select(); // 阻塞直到有事件
Set<SelectionKey> keys = selector.selectedKeys();
// 处理就绪的事件
}
4. 使用场景对比
| 场景 | 推荐 | 原因 |
|---|---|---|
| 少量连接,大文件 | IO | 编程简单,适合大文件读写 |
| 大量连接,小数据 | NIO | 高并发,非阻塞IO |
| 实时性要求高 | NIO | 非阻塞,响应快 |
| 开发效率优先 | IO | API简单,易于理解和维护 |
5. 代码示例对比
5.1 文件复制
// IO方式
public static void copyFileIO(File source, File dest) throws IOException {
try (InputStream in = new FileInputStream(source);
OutputStream out = new FileOutputStream(dest)) {
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}
}
// NIO方式
public static void copyFileNIO(File source, File dest) throws IOException {
try (FileChannel inChannel = new FileInputStream(source).getChannel();
FileChannel outChannel = new FileOutputStream(dest).getChannel()) {
inChannel.transferTo(0, inChannel.size(), outChannel);
// 或使用transferFrom
// outChannel.transferFrom(inChannel, 0, inChannel.size());
}
}
5.2 网络服务器
// BIO服务器 - 每个连接一个线程
public class BioServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8080);
while (true) {
Socket socket = server.accept();
new Thread(() -> handleRequest(socket)).start();
}
}
}
// NIO服务器 - 单线程处理多连接
public class NioServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // 等待事件
Set<SelectionKey> keys = selector.selectedKeys();
keys.forEach(key -> {
if (key.isAcceptable()) {
// 处理连接
} else if (key.isReadable()) {
// 处理读取
}
});
keys.clear();
}
}
}
6. 性能比较
| 指标 | IO | NIO |
|---|---|---|
| 线程模型 | 一个连接一个线程 | 少量线程处理多连接 |
| 内存使用 | 较高(线程栈) | 较低(少量线程) |
| 上下文切换 | 频繁 | 较少 |
| 数据处理 | 直接处理 | 需要buffer管理 |
7. 选择建议
-
选IO的情况:
-
连接数少(<1000)
-
每个连接数据量大
-
开发周期短
-
维护简单优先
-
-
选NIO的情况:
-
连接数多(>1000)
-
短连接,小数据
-
高并发场景
-
需要非阻塞操作
-
-
实际应用:
-
传统应用服务器(Tomcat早期版本)使用BIO
-
高性能服务器(Netty、Tomcat NIO模式)使用NIO
-
现代框架(Spring WebFlux)基于NIO
-
8. 常见问题
-
NIO的bug:早期版本有epoll空轮询bug
-
调试难度:NIO调试更复杂
-
代码复杂度:NIO代码更难维护
-
Buffer管理:需要小心处理buffer的flip、clear等操作
更多推荐

所有评论(0)