Java NIO系列教程(十六)Java NIO AsynchronousFileChannel
Java NIO 中的 AsynchronousFileChannel(异步文件通道)支持对文件进行异步读写操作,显著提升I/O效率。它提供两种数据读写模式:一是基于Future接口,调用后立即返回,可通过轮询检查操作状态;二是基于CompletionHandler回调,在操作完成或失败时自动触发相应方法。创建通道时需指定Path和操作选项(如READ/WRITE)。使用时需注意写入前确保目标文件存在,并通过ByteBuffer传递数据。该机制适用于需避免I/O阻塞的高性能应用场景。
博主博客
在 Java 7 中,Java NIO 新增了 AsynchronousFileChannel(异步文件通道)。它允许以异步方式从文件中读取数据或向文件写入数据。本教程将详细介绍如何使用 AsynchronousFileChannel。
创建 AsynchronousFileChannel
通过静态方法 open() 可以创建一个 AsynchronousFileChannel。以下是一个创建示例:
Path path = Paths.get("data/test.xml");
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.READ);
open() 方法的第一个参数是一个 Path 实例,指向 AsynchronousFileChannel 要关联的文件。
第二个参数是一个或多个打开选项,用于指定对底层文件执行的操作。上面的例子中使用了 StandardOpenOption.READ,表示以只读方式打开文件。
读取数据
有两种方式可以从 AsynchronousFileChannel 读取数据,每种方式都调用了 read() 方法。下面将分别介绍这两种方法。
通过 Future 读取数据
第一种方式是调用返回 Future 的 read() 方法。调用方式如下:
Future<Integer> operation = fileChannel.read(buffer, 0);
这个版本的 read() 方法以 ByteBuffer 作为第一个参数。从 AsynchronousFileChannel 读取的数据会存入该 ByteBuffer 中。第二个参数是文件开始读取的字节位置。
即使读取操作尚未完成,read() 方法也会立即返回。可以通过调用 read() 方法返回的 Future 实例的 isDone() 方法来检查读取操作是否完成。
下面是一个更完整的示例:
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
Future<Integer> operation = fileChannel.read(buffer, position);
while(!operation.isDone()); // 等待读取完成
buffer.flip();
byte[] data = new byte[buffer.limit()];
buffer.get(data);
System.out.println(new String(data));
buffer.clear();
这个例子创建了一个 AsynchronousFileChannel,然后创建了一个 ByteBuffer 并将其作为参数传递给 read() 方法,同时指定起始位置为 0。调用 read() 后,程序循环检查返回的 Future 的 isDone() 方法是否返回 true。请注意,这种循环等待的方式对 CPU 效率不高,但我们需要某种方式来等待读取操作完成。
一旦读取操作完成,数据会从 ByteBuffer 中提取出来,转换为字符串并打印到控制台。
通过 CompletionHandler 读取数据
第二种方式是通过调用接收 CompletionHandler 作为参数的 read() 方法来读取数据。调用方式如下:
fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("读取的字节数 = " + result);
attachment.flip();
byte[] data = new byte[attachment.limit()];
attachment.get(data);
System.out.println(new String(data));
attachment.clear();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
// 处理读取失败的情况
}
});
读取操作完成后,CompletionHandler 的 completed() 方法会被调用。completed() 方法的参数包括一个表示读取字节数的 Integer,以及传递给 read() 方法的“附件”(attachment)。在本例中,“附件”是用于存储数据的 ByteBuffer。你可以自由选择要附加的对象。
如果读取操作失败,CompletionHandler 的 failed() 方法将被调用。
写入数据
与读取数据类似,向 AsynchronousFileChannel 写入数据也有两种方式。每种方式都调用了 write() 方法。下面将分别介绍这两种方法。
通过 Future 写入数据
AsynchronousFileChannel 也支持异步写入数据。以下是一个完整的写入示例:
Path path = Paths.get("data/test-write.txt");
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("测试数据".getBytes());
buffer.flip();
Future<Integer> operation = fileChannel.write(buffer, position);
buffer.clear();
while(!operation.isDone()); // 等待写入完成
System.out.println("写入完成");
首先以写入模式打开一个 AsynchronousFileChannel。然后创建一个 ByteBuffer,并向其中写入一些数据。接着将 ByteBuffer 中的数据写入文件。最后通过检查返回的 Future 来确定写入操作何时完成。
注意:文件必须已经存在,否则 write() 方法会抛出 java.nio.file.NoSuchFileException 异常。
可以使用以下代码确保文件存在:
if(!Files.exists(path)) {
Files.createFile(path);
}
通过 CompletionHandler 写入数据
除了使用 Future,你还可以使用 CompletionHandler 来通知写入操作何时完成。示例如下:
Path path = Paths.get("data/test-write.txt");
if(!Files.exists(path)) {
Files.createFile(path);
}
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("测试数据".getBytes());
buffer.flip();
fileChannel.write(buffer, position, 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) {
System.out.println("写入失败");
exc.printStackTrace();
}
});
写入操作完成时,CompletionHandler 的 completed() 方法会被调用。如果写入失败,则会调用 failed() 方法。
注意示例中如何将 ByteBuffer 作为“附件”传递给 CompletionHandler 的方法。
参考文献
Java NIO系列教程(十六)Java NIO AsynchronousFileChannel
https://blog.uso6.com/archives/java-nioxi-lie-jiao-cheng-shi-liu-java-nio-asynchronousfilechannel
评论