AIO
字数: 0 字 时长: 0 分钟
AIO 通常指 Asynchronous I/O(异步 IO)。
Java 在 JDK 7 的 NIO.2 中提供了异步 IO API,常见的类包括:
AsynchronousFileChannel
AsynchronousSocketChannel
AsynchronousServerSocketChannelAIO 和 BIO、NIO 最大的区别在于:
程序提交 IO 操作以后,不需要一直等待结果,操作完成后再通过 Future 或回调方式获取结果。
简单理解:
BIO
↓
线程自己等 IO 完成
NIO
↓
线程等待 IO 事件准备好
↓
再自己完成读写
AIO
↓
提交 IO 操作
↓
继续执行其他任务
↓
IO 完成后通知程序1. BIO、NIO、AIO 的区别
BIO
BIO 是阻塞式 IO。
例如:
inputStream.read(buffer);如果没有数据:
线程
↓
阻塞等待
↓
直到有数据NIO
NIO 支持非阻塞模式。
通常配合:
Channel
Selector
Buffer使用。
例如:
多个 SocketChannel
↓
Selector
↓
一个线程监听Selector 发现某个 Channel:
可读
可写
可连接之后,程序再主动处理对应的 IO 操作。
AIO
AIO 是异步 IO。
程序发起一个 IO 操作:
发起 read()
↓
立即返回
↓
程序继续做其他事情
↓
IO 完成
↓
通过 Future / CompletionHandler 获取结果所以三者可以简单记成:
BIO
线程等 IO
NIO
线程等 IO 事件
AIO
IO 完成后通知线程2. Java AIO 核心类
Java AIO 中主要有三个核心类:
AIO
│
├── AsynchronousFileChannel
│ └── 异步文件 IO
│
├── AsynchronousSocketChannel
│ └── 异步 TCP 客户端
│
└── AsynchronousServerSocketChannel
└── 异步 TCP 服务端对应关系:
| 场景 | BIO / NIO | AIO |
|---|---|---|
| 文件 | FileChannel | AsynchronousFileChannel |
| TCP 客户端 | SocketChannel | AsynchronousSocketChannel |
| TCP 服务端 | ServerSocketChannel | AsynchronousServerSocketChannel |
3. Future 方式
AIO 支持通过:
Future获取异步操作结果。
例如异步读取文件:
AsynchronousFileChannel channel =
AsynchronousFileChannel.open(
Paths.get("test.txt"),
StandardOpenOption.READ
);
ByteBuffer buffer =
ByteBuffer.allocate(1024);
Future<Integer> future =
channel.read(
buffer,
0
);
System.out.println("IO 已提交,可以继续执行其他任务");
// 获取结果
Integer len =
future.get();
System.out.println(
"读取字节数:" + len
);
channel.close();执行过程:
channel.read()
↓
提交异步读取
↓
返回 Future
↓
程序继续执行
↓
future.get()
↓
获取读取结果需要注意:
future.get();如果 IO 还没有完成:
当前线程仍然会阻塞等待结果。
所以 Future 虽然可以表示异步任务结果,但如果马上:
future.get();那异步带来的意义就会减少。
4. CompletionHandler
AIO 还有一种更加典型的写法:
CompletionHandler也就是:
IO 操作完成后自动回调。
接口大致有两个方法:
completed()
failed()分别表示:
completed()
↓
操作成功
failed()
↓
操作失败5. AsynchronousFileChannel
AsynchronousFileChannel 用于异步操作文件。
例如:
AsynchronousFileChannel channel =
AsynchronousFileChannel.open(
Paths.get("test.txt"),
StandardOpenOption.READ
);
ByteBuffer buffer =
ByteBuffer.allocate(1024);
channel.read(
buffer,
0,
buffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(
Integer result,
ByteBuffer attachment
) {
System.out.println(
"读取完成,字节数:" + result
);
attachment.flip();
while (attachment.hasRemaining()) {
System.out.print(
(char) attachment.get()
);
}
}
@Override
public void failed(
Throwable exc,
ByteBuffer attachment
) {
exc.printStackTrace();
}
}
);这里:
channel.read()只是发起异步读取操作。
当读取完成后:
completed()会被调用。
如果操作失败:
failed()会被调用。
整个流程:
read()
↓
提交异步任务
↓
程序继续运行
↓
IO 完成
↓
completed()6. AsynchronousSocketChannel
AsynchronousSocketChannel 用于异步 TCP 客户端通信。
对应传统的:
Socket以及 NIO 的:
SocketChannel创建:
AsynchronousSocketChannel client =
AsynchronousSocketChannel.open();连接服务器:
client.connect(
new InetSocketAddress(
"localhost",
8080
)
);也可以通过 CompletionHandler 处理连接完成事件。
例如:
client.connect(
new InetSocketAddress(
"localhost",
8080
),
null,
new CompletionHandler<Void, Void>() {
@Override
public void completed(
Void result,
Void attachment
) {
System.out.println("连接成功");
}
@Override
public void failed(
Throwable exc,
Void attachment
) {
exc.printStackTrace();
}
}
);7. AsynchronousServerSocketChannel
AsynchronousServerSocketChannel 用于异步 TCP 服务端。
对应传统 IO:
ServerSocket以及 NIO:
ServerSocketChannel创建:
AsynchronousServerSocketChannel server =
AsynchronousServerSocketChannel.open();绑定端口:
server.bind(
new InetSocketAddress(8080)
);然后通过:
accept()异步等待客户端连接。
例如:
server.accept(
null,
new CompletionHandler<
AsynchronousSocketChannel,
Void
>() {
@Override
public void completed(
AsynchronousSocketChannel client,
Void attachment
) {
System.out.println("客户端连接成功");
// 继续接收下一个客户端
server.accept(
null,
this
);
}
@Override
public void failed(
Throwable exc,
Void attachment
) {
exc.printStackTrace();
}
}
);需要注意:
一次
accept()通常只处理一次连接完成事件。
所以在:
completed()中还需要再次:
server.accept()继续接收新的客户端。
8. AIO 的回调模型
AIO 的核心思想可以理解为:
程序
↓
提交 IO 操作
↓
不等待结果
↓
继续执行其他代码
↓
IO 完成
↓
触发回调
↓
处理结果例如:
AsynchronousSocketChannel.read()
↓
提交读取
↓
返回
↓
其他任务继续执行
↓
读取完成
↓
CompletionHandler.completed()所以和 NIO 的事件模型有所区别。
NIO:
Selector
↓
告诉你:这个 Channel 可以读了
↓
程序自己 read()AIO:
程序直接发起 read()
↓
读取完成
↓
通知程序结果9. AIO 一定是操作系统原生异步 IO 吗?
不一定。
Java AIO 提供的是:
异步 IO 编程模型。
但具体底层怎么实现,会受到:
操作系统
JDK 实现
平台能力等因素影响。
有的平台可能使用操作系统提供的异步 IO 能力,有的平台则可能通过:
线程池
后台线程等方式实现异步效果。
因此不能简单理解为:
AIO
=
一定由操作系统硬件直接完成更准确地说:
Java AIO 对程序暴露的是异步接口,底层具体实现由 JDK 和操作系统决定。
10. AIO 的优缺点
优点
AIO 的主要优点:
线程不需要一直等待 IO
↓
异步完成后通知程序
↓
减少阻塞等待对于:
大量连接
IO 操作耗时较长
异步处理等场景具有一定优势。
缺点
AIO 的编程模型相对复杂。
尤其大量使用:
CompletionHandler以后,很容易出现:
回调
↓
回调
↓
回调代码理解和维护成本会增加。
另外,AIO 的实际性能优势也依赖:
操作系统
JDK
具体场景并不是任何场景下都一定比 NIO 更快。
11. BIO、NIO、AIO 对比
| 对比 | BIO | NIO | AIO |
|---|---|---|---|
| 全称 | Blocking I/O | New I/O | Asynchronous I/O |
| 模型 | 阻塞 | 支持非阻塞 | 异步 |
| 网络核心 API | Socket | Channel + Selector | AsynchronousChannel |
| IO 等待 | 线程阻塞等待 | Selector 监听事件 | IO 完成后通知 |
| 典型处理方式 | 一个连接一个线程 | 一个线程管理多个 Channel | Future / 回调 |
| 编程复杂度 | 低 | 较高 | 较高 |
可以简单记成:
BIO
发起 IO
↓
等
↓
完成
NIO
Selector
↓
发现 IO 已就绪
↓
自己处理 IO
AIO
发起 IO
↓
继续干其他事情
↓
IO 完成
↓
通知程序12. BIO、NIO、AIO 的关系
整个 Java IO 模型可以简单串起来:
BIO
↓
阻塞 IO
↓
一个连接通常需要一个线程
↓
大量连接时线程资源压力大
↓
NIO
↓
Channel + Buffer + Selector
↓
非阻塞 + IO 多路复用
↓
少量线程管理多个连接
↓
AIO
↓
提交异步 IO
↓
完成后通过 Future / Callback 获取结果13. 小结
AIO 即:
Asynchronous I/OJava 在 NIO.2 中提供了:
AsynchronousFileChannel
AsynchronousSocketChannel
AsynchronousServerSocketChannel异步结果主要有两种处理方式:
Future以及:
CompletionHandler核心区别可以记成:
BIO
↓
线程等 IO
NIO
↓
线程通过 Selector 等事件
↓
事件就绪后自己处理 IO
AIO
↓
直接提交 IO
↓
IO 完成后通知程序一句话理解:
BIO 是“我等你做完”,NIO 是“你准备好了告诉我,我来做”,AIO 是“你直接做,做完以后告诉我结果”。