同步 IO vs 异步 IO
大多数人对「同步」和「异步」的理解是模糊的——只觉得「同步就是等,异步就是不等」,但说不清楚等的是什么、谁在等。
真相是:同步和异步的区别,不在于等不等,而在于数据拷贝由谁完成。
同步 IO:调用方自己干
调用 read() 时,进程从用户态进入内核态,内核把数据从磁盘拷贝到内核缓冲区,再拷贝到用户缓冲区,然后返回。整个拷贝过程调用方都在等待。
java
// 同步调用:read() 返回时,数据已经在你的 byte[] 里了
byte[] data = new byte[1024];
int len = fis.read(data); // 线程在这里卡住,直到数据就绪
// 继续往下执行时,data 里已经有数据了同步 IO 有两种状态:
- 同步阻塞(BIO):
read()没数据就一直等 - 同步非阻塞(NIO):
read()没数据立刻返回 -1,但有数据时仍然是你自己拷贝
异步 IO:交给别人干
调用 read() 后立即返回,不等待数据就绪。操作系统把数据拷贝到用户缓冲区完成后,通过回调或事件通知你。
java
// JDK 7+ 异步文件 IO 示例
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Path.of("data.txt"));
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
Future<Integer> result = channel.read(buffer, 0); // 立即返回,不阻塞
// 可以干别的事
doSomethingElse();
// 等数据就绪(阻塞在这里,或者检查 Future)
Integer bytesRead = result.get();JDK 7 的 AsynchronousFileChannel 有两种异步通知方式:
方式 1:Future 模式
java
Future<Integer> future = channel.read(buffer, 0);
while (!future.isDone()) {
// 做别的事
}
Integer result = future.get(); // 数据就绪后才返回方式 2:CompletionHandler 回调
java
channel.read(buffer, 0, buffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 数据读完了,在这里处理
System.out.println("读到了 " + result + " 字节");
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
// 读取失败
exc.printStackTrace();
}
});核心区别
同步 IO
调用 read() → 内核拷贝数据(你等着)→ 返回
异步 IO
调用 read()(附带回调)→ 立即返回(你可以干别的事)
→ 内核异步拷贝数据
→ 拷贝完成后回调通知你Java 中的异步 IO
| API | 包 | 说明 |
|---|---|---|
AsynchronousFileChannel | java.nio.channels | 文件异步 IO(JDK 7+) |
AsynchronousSocketChannel | java.nio.channels | Socket 异步 IO(JDK 7+) |
AsynchronousServerSocketChannel | java.nio.channels | 服务端异步 Socket(JDK 7+) |
注意:异步 IO 在 Java 中性能提升并不显著,实际用得不多。生产环境更多用 NIO + 多路复用或直接上 Netty。
最后送你一句话:同步和异步的区别,不在于等不等,而在于拷贝由谁完成。
