网站公告

本次更新:没啥更新的

“钱塘江上潮信起,今日方知我是我”

Skip to content

AIO ​

字数: 0 字 时长: 0 分钟

AIO 通常指 Asynchronous I/O(异步 IO)。

Java 在 JDK 7 的 NIO.2 中提供了异步 IO API,常见的类包括:

text
AsynchronousFileChannel
AsynchronousSocketChannel
AsynchronousServerSocketChannel

AIO 和 BIO、NIO 最大的区别在于:

程序提交 IO 操作以后,不需要一直等待结果,操作完成后再通过 Future 或回调方式获取结果。

简单理解:

text
BIO
    ↓
线程自己等 IO 完成

NIO
    ↓
线程等待 IO 事件准备好
    ↓
再自己完成读写

AIO
    ↓
提交 IO 操作
    ↓
继续执行其他任务
    ↓
IO 完成后通知程序

1. BIO、NIO、AIO 的区别 ​

BIO ​

BIO 是阻塞式 IO。

例如:

java
inputStream.read(buffer);

如果没有数据:

text
线程
 ↓
阻塞等待
 ↓
直到有数据

NIO ​

NIO 支持非阻塞模式。

通常配合:

text
Channel
Selector
Buffer

使用。

例如:

text
多个 SocketChannel
        ↓
     Selector
        ↓
   一个线程监听

Selector 发现某个 Channel:

text
可读
可写
可连接

之后,程序再主动处理对应的 IO 操作。


AIO ​

AIO 是异步 IO。

程序发起一个 IO 操作:

text
发起 read()
    ↓
立即返回
    ↓
程序继续做其他事情
    ↓
IO 完成
    ↓
通过 Future / CompletionHandler 获取结果

所以三者可以简单记成:

text
BIO
线程等 IO

NIO
线程等 IO 事件

AIO
IO 完成后通知线程

2. Java AIO 核心类 ​

Java AIO 中主要有三个核心类:

text
AIO
│
├── AsynchronousFileChannel
│     └── 异步文件 IO
│
├── AsynchronousSocketChannel
│     └── 异步 TCP 客户端
│
└── AsynchronousServerSocketChannel
      └── 异步 TCP 服务端

对应关系:

场景BIO / NIOAIO
文件FileChannelAsynchronousFileChannel
TCP 客户端SocketChannelAsynchronousSocketChannel
TCP 服务端ServerSocketChannelAsynchronousServerSocketChannel

3. Future 方式 ​

AIO 支持通过:

java
Future

获取异步操作结果。

例如异步读取文件:

java
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();

执行过程:

text
channel.read()
    ↓
提交异步读取
    ↓
返回 Future
    ↓
程序继续执行
    ↓
future.get()
    ↓
获取读取结果

需要注意:

java
future.get();

如果 IO 还没有完成:

当前线程仍然会阻塞等待结果。

所以 Future 虽然可以表示异步任务结果,但如果马上:

java
future.get();

那异步带来的意义就会减少。


4. CompletionHandler ​

AIO 还有一种更加典型的写法:

java
CompletionHandler

也就是:

IO 操作完成后自动回调。

接口大致有两个方法:

java
completed()

failed()

分别表示:

text
completed()
    ↓
操作成功

failed()
    ↓
操作失败

5. AsynchronousFileChannel ​

AsynchronousFileChannel 用于异步操作文件。

例如:

java
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();
            }
        }
);

这里:

java
channel.read()

只是发起异步读取操作。

当读取完成后:

java
completed()

会被调用。

如果操作失败:

java
failed()

会被调用。

整个流程:

text
read()
 ↓
提交异步任务
 ↓
程序继续运行
 ↓
IO 完成
 ↓
completed()

6. AsynchronousSocketChannel ​

AsynchronousSocketChannel 用于异步 TCP 客户端通信。

对应传统的:

text
Socket

以及 NIO 的:

text
SocketChannel

创建:

java
AsynchronousSocketChannel client =
        AsynchronousSocketChannel.open();

连接服务器:

java
client.connect(
        new InetSocketAddress(
                "localhost",
                8080
        )
);

也可以通过 CompletionHandler 处理连接完成事件。

例如:

java
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:

text
ServerSocket

以及 NIO:

text
ServerSocketChannel

创建:

java
AsynchronousServerSocketChannel server =
        AsynchronousServerSocketChannel.open();

绑定端口:

java
server.bind(
        new InetSocketAddress(8080)
);

然后通过:

java
accept()

异步等待客户端连接。

例如:

java
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() 通常只处理一次连接完成事件。

所以在:

java
completed()

中还需要再次:

java
server.accept()

继续接收新的客户端。


8. AIO 的回调模型 ​

AIO 的核心思想可以理解为:

text
程序
 ↓
提交 IO 操作
 ↓
不等待结果
 ↓
继续执行其他代码
 ↓
IO 完成
 ↓
触发回调
 ↓
处理结果

例如:

text
AsynchronousSocketChannel.read()
        ↓
提交读取
        ↓
返回
        ↓
其他任务继续执行
        ↓
读取完成
        ↓
CompletionHandler.completed()

所以和 NIO 的事件模型有所区别。

NIO:

text
Selector
    ↓
告诉你:这个 Channel 可以读了
    ↓
程序自己 read()

AIO:

text
程序直接发起 read()
    ↓
读取完成
    ↓
通知程序结果

9. AIO 一定是操作系统原生异步 IO 吗? ​

不一定。

Java AIO 提供的是:

异步 IO 编程模型。

但具体底层怎么实现,会受到:

text
操作系统
JDK 实现
平台能力

等因素影响。

有的平台可能使用操作系统提供的异步 IO 能力,有的平台则可能通过:

text
线程池
后台线程

等方式实现异步效果。

因此不能简单理解为:

text
AIO
=
一定由操作系统硬件直接完成

更准确地说:

Java AIO 对程序暴露的是异步接口,底层具体实现由 JDK 和操作系统决定。


10. AIO 的优缺点 ​

优点 ​

AIO 的主要优点:

text
线程不需要一直等待 IO
    ↓
异步完成后通知程序
    ↓
减少阻塞等待

对于:

text
大量连接
IO 操作耗时较长
异步处理

等场景具有一定优势。

缺点 ​

AIO 的编程模型相对复杂。

尤其大量使用:

java
CompletionHandler

以后,很容易出现:

text
回调
 ↓
回调
 ↓
回调

代码理解和维护成本会增加。

另外,AIO 的实际性能优势也依赖:

text
操作系统
JDK
具体场景

并不是任何场景下都一定比 NIO 更快。


11. BIO、NIO、AIO 对比 ​

对比BIONIOAIO
全称Blocking I/ONew I/OAsynchronous I/O
模型阻塞支持非阻塞异步
网络核心 APISocketChannel + SelectorAsynchronousChannel
IO 等待线程阻塞等待Selector 监听事件IO 完成后通知
典型处理方式一个连接一个线程一个线程管理多个 ChannelFuture / 回调
编程复杂度低较高较高

可以简单记成:

text
BIO

发起 IO
 ↓
等
 ↓
完成


NIO

Selector
 ↓
发现 IO 已就绪
 ↓
自己处理 IO


AIO

发起 IO
 ↓
继续干其他事情
 ↓
IO 完成
 ↓
通知程序

12. BIO、NIO、AIO 的关系 ​

整个 Java IO 模型可以简单串起来:

text
BIO
 ↓
阻塞 IO
 ↓
一个连接通常需要一个线程
 ↓
大量连接时线程资源压力大
 ↓
NIO
 ↓
Channel + Buffer + Selector
 ↓
非阻塞 + IO 多路复用
 ↓
少量线程管理多个连接
 ↓
AIO
 ↓
提交异步 IO
 ↓
完成后通过 Future / Callback 获取结果

13. 小结 ​

AIO 即:

text
Asynchronous I/O

Java 在 NIO.2 中提供了:

text
AsynchronousFileChannel
AsynchronousSocketChannel
AsynchronousServerSocketChannel

异步结果主要有两种处理方式:

text
Future

以及:

text
CompletionHandler

核心区别可以记成:

text
BIO
    ↓
线程等 IO


NIO
    ↓
线程通过 Selector 等事件
    ↓
事件就绪后自己处理 IO


AIO
    ↓
直接提交 IO
    ↓
IO 完成后通知程序

一句话理解:

BIO 是“我等你做完”,NIO 是“你准备好了告诉我,我来做”,AIO 是“你直接做,做完以后告诉我结果”。

Noctiflorous Garden · MIT License