在Java高性能网络编程领域,BIO、NIO、AIO 是最常见的三种I/O模型。理解它们的原理、差异及应用场景,对于选择合适的技术方案、提升系统性能具有重要意义。本文将一次性为你梳理这三种技术的实现原理、常用代码以及最佳实践场景。
一、BIO(Blocking IO)——阻塞式IO
1. 原理简介
BIO即“Blocking IO”,即传统的阻塞式编程模型。每当有一个客户端请求,服务器都需要创建一个独立的线程进行处理,线程与连接一一对应。在读写操作时,线程会被阻塞,直到数据读/写完成。
特点
- 同步、阻塞:没有数据的时候线程会原地等待。
- 线程模型简单:一个连接一个线程,容易编程。
- 资源消耗大,扩展性差:大量连接下需要大量线程。
2. 应用场景
- 并发量小,业务模型简单,如传统的管理后台、低并发RPC服务。
- 对延迟或大规模并发请求不敏感的应用。
3. BIO代码示例
服务端
import java.io.*;
import java.net.*;
public class BioServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("BIO服务器启动,端口8080...");
while (true) {
final Socket client = serverSocket.accept(); // 阻塞,直到有客户端连接
new Thread(() -> {
try (
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
PrintWriter writer = new PrintWriter(out, true);
) {
String line;
while ((line = reader.readLine()) != null) { // 阻塞
System.out.println("收到客户端:" + line);
writer.println("echo:" + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start(); // 每个连接分配一个线程
}
}
}
客户端
import java.io.*;
import java.net.*;
public class BioClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 8080);
try (
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
) {
writer.write("hello, server!\n");
writer.flush();
String response = reader.readLine();
System.out.println("收到服务器:" + response);
}
}
}
二、NIO(Non-blocking IO)——非阻塞式IO
1. 原理简介
NIO是Java 1.4引入的“非阻塞式IO”库,又称New IO或Non-blocking IO。与BIO线程一对一不同,NIO采用单线程+多路复用,基于Selector监听多个Channel(通道),只需少量线程即可处理大量连接。
关键组件
- Channel(通道):类似流,但可读写和异步操作。
- Buffer(缓冲区):用于存储读/写数据。
- Selector(选择器):核心,监控多个通道的事件(如可读、可写)。
特点
- 非阻塞:调用read/write不会一直阻塞。
- 单线程可处理成千上万连接,而不是“一线程一连接”。
- 编码复杂,需要手动事件分派和状态管理。
2. 应用场景
- 高并发服务器端程序,如IM、网关、游戏服务器、分布式高性能RPC等。
- 需要处理成千上万连接,但连接每秒请求有限的"海量连接、少量数据"场景。
3. NIO代码示例
服务端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
public class NioServer {
public static void main(String[] args) throws Exception {
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(8081));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO服务器启动,端口8081...");
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
selector.select(); // 阻塞,直到有事件到来
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel client = ssc.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("有新连接进来...");
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
buffer.clear();
int len = client.read(buffer);
if (len > 0) {
buffer.flip();
byte[] data = new byte[len];
buffer.get(data, 0, len);
System.out.println("收到客户端数据: " + new String(data));
// 回写
buffer.clear();
buffer.put(("echo:" + new String(data)).getBytes());
buffer.flip();
client.write(buffer);
} else if (len == -1) {
client.close();
}
}
}
}
}
}
客户端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NioClient {
public static void main(String[] args) throws Exception {
SocketChannel client = SocketChannel.open(new InetSocketAddress("localhost", 8081));
client.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.wrap("hello, nio!\n".getBytes());
client.write(buffer);
buffer.clear();
Thread.sleep(100); // 等待服务器回写
client.read(buffer);
buffer.flip();
System.out.println("收到服务器:" + new String(buffer.array()).trim());
client.close();
}
}
三、AIO(Asynchronous IO)——异步非阻塞式IO
1. 原理简介
AIO又称NIO.2,是Java 7引入的“异步IO”模型。AIO底层由操作系统支持(如Linux epoll+线程池,Windows IOCP),读写操作不再等待完成,通过回调函数完成后得知结果。
特点
- 真正的异步、非阻塞:读写时立即返回,通过操作系统底层机制或线程池处理完后自动回调。
- 代码难度进一步提高,但极度利于海量并发和异步操作。
- 对操作系统支持依赖较大。
2. 应用场景
- 超高并发、对响应延迟极为敏感的服务器应用,如分布式消息服务、推送服务器等。
- 多为底层高并发框架或Netty等中间件底层使用;实际业务开发直接用AIO较少。
3. AIO代码示例
服务端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.TimeUnit;
public class AioServer {
public static void main(String[] args) throws Exception {
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8082));
System.out.println("AIO服务器启动,端口8082...");
// 接收连接
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel client, Object attachment) {
server.accept(null, this); // 递归接受下一个
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer, 30, TimeUnit.SECONDS, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
String content = new String(attachment.array(), 0, result);
System.out.println("收到客户端:" + content);
// 回复
ByteBuffer outBuf = ByteBuffer.wrap(("echo:" + content).getBytes());
client.write(outBuf, outBuf, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
try { client.close(); } catch (Exception ignored) {}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {}
});
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
exc.printStackTrace();
}
});
// 主线程阻塞
Thread.currentThread().join();
}
}
客户端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future;
public class AioClient {
public static void main(String[] args) throws Exception {
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
Future<Void> future = client.connect(new InetSocketAddress("localhost", 8082));
future.get();
client.write(ByteBuffer.wrap("hello, aio!\n".getBytes())).get();
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer).get();
buffer.flip();
System.out.println("收到服务器:" + new String(buffer.array(), 0, buffer.remaining()));
client.close();
}
}
四、三者对比总结
| BIO | NIO | AIO | |
|---|---|---|---|
| 是否阻塞 | 阻塞 | 非阻塞 | 非阻塞/异步 |
| 线程模型 | 一连接一线程/多个线程 | 单线程或少量线程+Selector | 完全异步(事件驱动) |
| 编程难度 | 简单 | 中等 | 较高 |
| 性能扩展 | 差:线程资源消耗大 | 较好(能应对上万连接) | 极优 |
| 适用场景 | 小并发后台,老系统 | 高并发服务器、IM、网关等 | 超高并发、低延迟 |
| 支持平台 | 通用 | 通用 | 需OS底层支持 |
五、实际应用建议
- BIO:小型项目、后台管理等低并发场景即可。
- NIO:当今主流网络服务器的基础,Netty、Tomcat底层都用NIO,适合大部分高并发应用。
- AIO:在极致并发/异步需求下选用,多用于分布式消息中间件、底层高性能中间件或Netty底层组件,不建议普通业务直接用。
2476

被折叠的 条评论
为什么被折叠?



