Java中 BIO,NIO,AIO 总结
本文围绕 Java 网络编程中常见的 BIO、NIO 与 AIO 三种 I/O 模型展开总结。文章首先从同步与异步、阻塞与非阻塞两个基础概念入手,梳理不同 I/O 模型在调用方式与线程行为上的本质差异;随后结合 Java 标准库,对 BIO、NIO 与 AIO 的核心机制、工作流程及适用场景进行了对比分析,并通过典型使用方式说明其在不同并发规模下的优缺点。
目录
在讲 BIO,NIO,AIO 之前先来回顾一下这样几个概念:同步与异步,阻塞与非阻塞。
1.同步与异步
-
同步调用(Synchronous Cal): 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回
代码示例:
int result = calculateSum(3, 5); // 等到函数调用完成后,获取返回结果
System.out.println(result); // 输出计算结果 8
public static int calculateSum(int a, int b) {
// 同步调用:当前线程会阻塞,直到函数计算完成并返回结果
return a + b;
}
-
异步调用(Asynchronous Call): 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
代码示例(使用回调): 调用者传递一个函数给被调用者,被调用者在完成后调用该函数。
interface Callback {
void onComplete(int result);
}
void calculateSumAsync(int a, int b, Callback callback) {
new Thread(() -> {
int result = a + b;
callback.onComplete(result); // 完成后通知调用者
}).start();
}
// 调用
calculateSumAsync(3, 5, (result) -> System.out.println("结果: " + result));
代码示例(使用事件):被调用者通过触发事件(如按钮点击、网络请求完成)通知调用者。
class EventEmitter {
private List<EventListener> listeners = new ArrayList<>();
void addListener(EventListener listener) {
listeners.add(listener);
}
void doWork() {
new Thread(() -> {
int result = 42;
for (EventListener listener : listeners) {
listener.onEvent(result); // 触发事件
}
}).start();
}
}
interface EventListener {
void onEvent(int data);
}
// 调用
EventEmitter emitter = new EventEmitter();
emitter.addListener(data -> System.out.println("收到事件: " + data));
emitter.doWork();
同步调用中调用者发出请求后,必须阻塞等待被调用者完成处理并返回结果,异步调用中调用者发出请求后立即获得"已接收"响应,但此时可能尚未得到最终处理结果
2.阻塞和非阻塞
-
阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
-
非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
阻塞是指线程在等待某个条件或资源时被挂起,不能继续执行;而非阻塞是指线程不会被挂起,操作立即返回,能快速判断是否成功继续执行。阻塞常用于简单同步场景,但性能较低;非阻塞适合高并发环境,效率更高。
同/异步与阻塞/非阻塞区别:
同步与异步主要关注的是调用结果的获取方式:同步调用中,调用方在发起请求后需要等待操作完成并直接获得返回结果;而异步调用中,调用方发起请求后无需立刻获取结果,结果会在任务完成后通过回调、Future 或事件通知等方式返回。
阻塞与非阻塞主要关注的是等待期间线程的执行状态:阻塞表示线程在等待结果时被挂起,无法执行其他任务;非阻塞表示线程在等待期间不会被挂起,仍可以继续执行其他逻辑。
3.BIO,NIO,AIO
3.1 BIO (Blocking I/O)
BIO 是 Java 早期最经典的 I/O 模型,核心特征是 同步 + 阻塞:线程发起读/写后,会一直阻塞等待数据就绪以及拷贝完成,期间不能做其他事情。在服务端模型中,常见写法是 一个连接对应一个线程(或线程池),实现简单但并发高时线程开销大、容易出现上下文切换和资源耗尽问题。
// BIO 服务端:阻塞式接收 + 阻塞式读取(一个连接一个线程示例)
import java.io.*;
import java.net.*;
public class BioServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(8080);
System.out.println("BIO server start...");
while (true) {
Socket socket = server.accept(); // 阻塞:等待连接
new Thread(() -> {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line = in.readLine(); // 阻塞:等待数据
out.println("BIO reply: " + line);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
适用场景:低并发、连接数不多、开发快速验证;或业务处理很重、连接少(线程数可控)。
3.2 NIO (No-Blocking I/O)
NIO 是 Java 1.4 引入的 I/O 框架,对应 java.nio 包。NIO 的核心是 同步 + 非阻塞:读写调用可以立即返回(可能读到 0 字节),应用通过 Selector 多路复用在一个线程内管理多个连接,减少线程数量与上下文切换成本。NIO 采用 Buffer(缓冲区)+ Channel(通道)的方式进行数据读写,并支持阻塞/非阻塞两种模式,但在高并发网络编程中通常使用 非阻塞 + Selector。
// NIO 服务端:Selector 多路复用(单线程处理多个连接)
import java.io.IOException;
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 {
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8081));
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO server start...");
ByteBuffer buf = ByteBuffer.allocate(1024);
while (true) {
selector.select(); // 阻塞等待“就绪事件”(可认为等待通知)
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
SocketChannel sc = server.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
buf.clear();
int n = sc.read(buf); // 非阻塞读:可能返回 0 或 -1
if (n <= 0) {
sc.close();
continue;
}
buf.flip();
sc.write(ByteBuffer.wrap(("NIO reply: ").getBytes()));
sc.write(buf);
}
}
}
}
}
适用场景:高并发连接、多连接 I/O 频繁(网络服务器、网关、聊天室等),希望用少量线程处理大量连接。
3.3 AIO (Asynchronous I/O)
AIO(也常称 NIO.2)在 Java 7 引入,对应 java.nio.channels.Asynchronous 系列。AIO 的核心特征是 异步 + 非阻塞:应用发起 I/O 操作后立即返回,不需要自己轮询或等待,操作完成后由系统通过回调(CompletionHandler)或 Future 通知结果。相比 NIO 的“同步非阻塞(事件就绪后自己读写)”,AIO 更接近“真正的异步”:你只需要提交任务,完成后得到通知。
// AIO 服务端:异步 accept + 异步 read(回调驱动)
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.CountDownLatch;
public class AioServer {
public static void main(String[] args) throws Exception {
AsynchronousServerSocketChannel server =
AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8082));
System.out.println("AIO server start...");
CountDownLatch latch = new CountDownLatch(1);
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel ch, Void att) {
// 继续接收下一个连接(非常关键,否则只接一次)
server.accept(null, this);
ByteBuffer buf = ByteBuffer.allocate(1024);
ch.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer n, ByteBuffer b) {
try {
if (n <= 0) { ch.close(); return; }
b.flip();
ch.write(ByteBuffer.wrap("AIO reply: ".getBytes())).get();
ch.write(b).get();
ch.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, ByteBuffer b) {
try { ch.close(); } catch (Exception ignored) {}
}
});
}
@Override
public void failed(Throwable exc, Void att) {
exc.printStackTrace();
latch.countDown();
}
});
latch.await(); // 防止主线程退出
}
}
BIO、AIO、NIO小结:
BIO:同步阻塞,简单但并发高会吃线程
NIO:同步非阻塞,Selector 管理多连接,适合高并发
AIO:异步非阻塞,回调/通知获取结果,编程模型更异步
更多推荐



所有评论(0)