Java NIO和Java AIO的区别解析
一、Java NIO 是 NIO,Java AIO 才是 AIO
"Java NIO 不是 AIO!
Java 有 3 套 I/O API:
- 传统 I/O(java.io)= BIO(同步阻塞)
- Java NIO(java.nio,1.4+)= NIO(同步非阻塞)
- Java AIO(AsynchronousChannel,1.7+)= AIO(异步非阻塞)
Java NIO 是 NIO——底层用 epoll。Java AIO 是 AIO"——实际用得很少。"
二、Java 3 大 I/O API 严格分类
┌──────────────────────────────────────────────────┐ │ Java 3 大 I/O API 严格分类 │ ├──────────────────────────────────────────────────┤ │ │ │ 1. 传统 I/O(java.io) │ │ └─ 同步阻塞 I/O = BIO │ │ └─ 老项目 / 简单场景 │ │ │ │ │ 2. Java NIO(java.nio,1.4+) │ │ └─ 同步非阻塞 I/O = NIO │ │ └─ 多路复用 + Channel + Buffer + Selector │ │ └─ ⚠️ 底层 Linux 用 epoll(事件驱动回调) │ │ └─ ⚠️ **同步**(用户线程要查询 I/O 状态) │ │ └─ Spring Cloud Gateway / Netty / Redis / Kafka│ │ │ │ 3. Java AIO(AsynchronousChannel,1.7+) │ │ └─ 异步非阻塞 I/O = AIO │ │ └─ 异步回调 / Future │ │ └─ ❌ 实际用得很少(**Linux 上 AIO 不如 NIO**)│ │ │ └──────────────────────────────────────────────────┘ 
三、Java NIO vs Java AIO 4 大核心区别
| 维度 | Java NIO | Java AIO |
|---|---|---|
| 全称 | New I/O | Asynchronous I/O |
| 引入版本 | Java 1.4(2002) | Java 1.7(2011) |
| 同步性 | 同步(用户线程查询 I/O 状态) | 异步(OS 通知用户线程) |
| 阻塞性 | 非阻塞(用户线程立即返回) | 非阻塞(用户线程立即返回) |
| 通知方式 | epoll 事件驱动(用户线程要 selector.select() 查询) | OS 异步回调(用户线程不用查询) |
| 类 | Channel + Buffer + Selector | AsynchronousChannel + CompletionHandler / Future |
| 底层 | epoll(Linux) | AIO(Linux)/ IOCP(Windows) |
| 性能(Linux) | ⚠️⚠️⚠️ 极好 | ⚠️ 反而不如 NIO |
| 使用场景 | 主流(高并发) | 理论强,实际不用 |
四、"epoll 是回调"为什么对(NIO 不是 AIO)
4.1 NIO 的"事件驱动" ≠ AIO 的"异步回调"
关键差异:
NIO(同步非阻塞):
用户线程:selector.select() ← ⚠️ 用户线程主动查询
↓
epoll_wait() 返回就绪的 fd
↓
用户线程遍历处理
↓
⚠️ 用户线程主动查询(同步)
AIO(异步非阻塞):
用户线程:channel.read(buffer, callback) ← ⚠️ 用户线程不查询
↓
OS 内核后台读
↓
读完后 OS 内核**主动回调** callback ← ⚠️ OS 主动回调(异步)注意:
- NIO 的 epoll = OS 内核通知用户线程有 fd 就绪
- 但用户线程还要主动 select() 查询(同步)
- AIO = OS 内核主动回调(真正异步)
- NIO 的"回调" ≠ AIO 的"回调"
4.2 用代码严格对比
// ========== NIO(同步非阻塞)==========
// 老哥 MOVA / Gateway 用的
Selector selector = Selector.open();
while (true) {
// ⚠️ 用户线程主动查询
int ready = selector.select(); // 用户线程主动调用
if (ready == 0) continue;
// 用户线程处理
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
// 处理就绪的 fd
}
}
// ⚠️ 用户线程主动 select() —— 同步
// ========== AIO(异步非阻塞)==========
// 老哥基本不用
AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
// ⚠️ 用户线程不查询,OS 回调
channel.read(buffer, attachment, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer len, ByteBuffer buf) {
// ⚠️ OS 内核完成后主动回调
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
// ⚠️ OS 内核失败时主动回调
}
});
// ⚠️ 用户线程不查询 —— 真正异步五、为什么 Java NIO 在 Linux 上比 Java AIO 性能好(核心)
5.1 根本原因
Linux 内核 AIO 实现: - ❌ 不成熟(glibc AIO 性能差) - ❌ 只支持 O_DIRECT 模式(绕过 Page Cache) - ❌ 不能用于普通文件 I/O - ❌ 不能用于网络 I/O Linux 内核 epoll 实现: - ✅ 成熟稳定(30+ 年历史) - ✅ 支持文件 / 网络 / 设备 / 管道 - ✅ 性能极佳(O(1))
核心洞察:
- Linux 上 AIO 不成熟(glibc AIO 性能差)
- Linux 上 epoll 极好(30+ 年优化)
- 所以 Java NIO(基于 epoll)性能 > Java AIO(基于 Linux AIO)
- MOVA / Gateway / Redis / Kafka 都不用 AIO——用 NIO + epoll
5.2 实际数据
| 指标 | Java NIO + epoll | Java AIO |
|---|---|---|
| 并发连接 | 10w+ | 1w-(实际上不如 NIO) |
| 吞吐量 | ⚠️⚠️⚠️ 极好 | ⚠️ 一般 |
| 延迟 | 微秒级 | 微秒级(Linux 不好) |
| 项目 | ✅ Gateway / Redis / Kafka | ❌ 不用 |
六、3 大 I/O API 真实使用情况
| API | 真实使用情况 |
|---|---|
| 传统 I/O(java.io) | 所有老项目 |
| Java NIO(java.nio) | 现代 Java 主流 |
| Java AIO(AsynchronousChannel) | 基本不用 |
七、3 大 I/O API 4 大核心差异
| 维度 | 传统 I/O | Java NIO | Java AIO |
|---|---|---|---|
| 包 | java.io | java.nio | java.nio.channels.AsynchronousXxx |
| 模型 | 同步阻塞(BIO) | 同步非阻塞(NIO) | 异步非阻塞(AIO) |
| 核心类 | Stream / Socket | Channel / Buffer / Selector | AsynchronousChannel / CompletionHandler |
| 项目 | 老项目 | Gateway / Redis / Kafka | ❌ 不用 |
八、项目实战对照
8.1 NIO 实战
| 项目 | 实现方式 | 语言 | 用了什么 |
|---|---|---|---|
| Spring Cloud Gateway | Netty 封装 Java NIO(epoll) | Java | Netty + Java NIO + epoll |
| Redis 6.0+ | C 语言原生实现(不用 Java NIO) | C | ae + epoll(不用 Java NIO) |
| Kafka 1.0+ | 直接用 Java NIO(没有 Netty 封装) | Java | Java NIO + epoll(不用 Netty) |
8.3 AIO 实战
- ❌ 不用(Linux 上 AIO 不如 NIO)
- 只在 Windows 上有性能优势(IOCP)
九、Java AIO 4 大核心 API(了解即可)
// 1. AsynchronousFileChannel(异步文件 I/O)
AsynchronousFileChannel channel = AsynchronousFileChannel.open(
Paths.get("test.txt"),
StandardOpenOption.READ
);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> result = channel.read(buffer, 0);
// ⚠️ 异步返回 Future
result.get(); // 阻塞等待完成(**不推荐**)
// 2. AsynchronousSocketChannel(异步 TCP 客户端)
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
Future<Void> connectFuture = client.connect(new InetSocketAddress("localhost", 8080));
// 3. AsynchronousServerSocketChannel(异步 TCP 服务端)
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel client, Void attachment) {
// 异步回调
}
});
// 4. AsynchronousDatagramChannel(异步 UDP)
AsynchronousDatagramChannel udpChannel = AsynchronousDatagramChannel.open();注意:
- Java AIO API 复杂(CompletionHandler / Future 两套 API)
- 实际项目用得少
- 面试了解即可
十、7 大追问完整串起来
1. 什么是轮询? └─ 软件层面:客户端反复问服务器 2. NIO 多路复用中的轮询? └─ OS 层面:select/poll 是真轮询,epoll 是事件驱动回调 3. NIO 的 Channel 里有多 BIO 吗? └─ ❌ 没有,Channel 是 fd 的 Java 包装 4. 一个 Selector 同一时间能处理几个 Channel? └─ 理论上无限个,实际上同时 1 个(单线程串行) 5. 什么是 fd(File Descriptor)? └─ Linux 内核的整数 ID └─ 一切 I/O 都是 fd └─ NIO Channel = fd 的 Java 包装 6. Java NIO 用 epoll 还是 select/poll? └─ Linux 下默认 epoll(**不是 select/poll**) └─ epoll 是事件驱动(**回调**),不是轮询 └─ 老哥说"epoll 是回调"完全正确 ✅ 7. Java NIO 是 AIO 吗? └─ ❌ **不是**!Java NIO 是 NIO,Java AIO 才是 AIO └─ Java NIO 是**同步**非阻塞(用户线程要 select() 查询) └─ Java AIO 是**异步**非阻塞(OS 内核**主动回调**) └─ Java AIO 实际不用(**Linux 上 AIO 不如 NIO**)
十一、NIO vs AIO 最终版
"Java NIO 不是 AIO!
Java 有 3 套 I/O API:
- 传统 I/O(java.io)= BIO(同步阻塞)
- Java NIO(java.nio)= NIO(同步非阻塞 + epoll 事件驱动)
- Java AIO(AsynchronousChannel)= AIO(异步非阻塞)
NIO 和 AIO 的核心区别:
- NIO = 用户线程主动 select() 查询(同步)
- AIO = OS 内核主动回调(真正异步)
Gateway / Redis / Kafka 用的都是 Java NIO——不是 AIO。 Java AIO 实际不用——Linux 上 AIO 性能不如 NIO(glibc AIO 不成熟)。"
到此这篇关于Java NIO和Java AIO的区别解析的文章就介绍到这了,更多相关Java NIO和AIO区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
idea创建springboot项目(版本只能选择17和21)的解决方法
idea2023创建spring boot项目时,java版本无法选择11,本文主要介绍了idea创建springboot项目(版本只能选择17和21),下面就来介绍一下解决方法,感兴趣的可以了解一下2024-01-01
Java中spring boot 字符串判断是否为空方法小结
这篇文章主要介绍了Java中spring boot字符串判断是否为空,通过安装依赖,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧2023-11-11
Springboot集成MongoDB无认证与开启认证的配置方式
本文主要介绍了Springboot集成MongoDB无认证与开启认证的配置方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2024-03-03


最新评论