在 Java 7 中,Java NIO 新增了 AsynchronousFileChannel
类。AsynchronousFileChannel
类使得数据可以进行异步读写。本文将介绍 AsynchronousFileChannel
的使用。
阅读文章的过程中如果有任何疑问,欢迎添加笔者为好友,拉您进【七日书摘】微信交流群,一起交流技术,一起打造高质量的职场技术交流圈子,抱团取暖,共同进步。
创建 AsynchronousFileChannel(Creating an AsynchronousFileChannel)
可以通过 AsynchronousFileChannel
的 open() 静态方法创建。下面是创建 AsynchronousFileChannel
的示例:
Path path = Paths.get("data/test.xml");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
open()
方法的第一个参数是一个 Path 实例,指向需要操作的文件。
第二个参数是一个或多个打开操作类型选项,告诉 AsynchronousFileChannel
要对文件执行那些类型的操作。上述示例中,我们使用 StandardOpenOption.READ
,表示以读的形式操作文件。
读取数据(Reading Data)
可以通过两种方式从 AsynchronousFileChannel
中读取数据。每种方法都会调用 AsynchronousFileChannel
的一个 read()
方法。下面将分别介绍这两种写法。
通过 Future 读取数据(Reading Data Via a Future)
从 AsynchronousFileChannel
读取数据的第一种方法是调用返回 Future 的 read() 方法。下面是调用 read() 方法的方式:
Future<Integer> operation = fileChannel.read(buffer, 0);
该版本的 read() 方法将 ByteBuffer 作为第一个参数,从 AsynchronousFileChannel
中读取的数据会被读入到 ByteBuffer 中。第二个参数是开始读取数据的字节位置。
read() 方法会立即返回,即使读取操作尚未完成,可以通过调用 read() 方法返回的 Future 实例的 isDone() 方法来检查读取操作是否完成。
下面是一个略长的示例,演示该版本的 read() 方法的使用:
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。调用 fileChannel.read(buffer, position)
后创建了一个循环来检查 Future 的 isDone() 方法返回是否为 true(即检查是否读取完毕)。当然,这不是一个非常有效的CPU使用-但不知何故,你需要等到读操作完成。
读取操作完成后,将数据读入 ByteBuffer,然后读入字符串并使用 System.out
输出。
通过 CompletionHandler 读取数据(Reading Data Via a CompletionHandler)
从 AsynchronousFileChannel 读取数据的第二种方法是调用以 CompletionHandler 作为参数的 read() 方法版本。下面是示例如何调用 read() 方法:
fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("result = " + 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() 方法。并传入一个 Integer 和 ByteBuffer 参数,前面的 Integer 表示是读取到的字节数大小,第二 ByteBuffer 也可以换成其他合适的对象方便数据写入。
如果读取操作失败了,将会调用 CompletionHandler
的 failed()
方法。
写数据(Writing Data)
与读取一样,您可以用两种方式将数据写入 AsynchronousFileChannel
,写入数据时调用不同的 write()
方法。以下部分将介绍两种数据写入方法。
通过 Future 写数据(Writing Data Via a Future)
通过 AsynchronousFileChannel 可以异步写入数据。下面是一个 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("test data".getBytes());
buffer.flip();
Future<Integer> operation = fileChannel.write(buffer, position);
buffer.clear();
while(!operation.isDone());
System.out.println("Write done");
首先 AsynchronousFileChannel
以写模式打开,接着创建一个 ByteBuffer
并将一些数据写入其中,再把 ByteBuffer
中的数据写入文件。最后检查返回的 Future
写入操作是否已完成。
这里需要注意,该文件(本作者注:data/test-write.txt
)必须是已经存在的,然后以上代码才能正常工作,否则 write() 方法将抛出java.nio.file.NoSuchFileException
。
检查文件是否存在可以通过以下的示例代码实现:
if(!Files.exists(path)){
Files.createFile(path);
}
通过 CompletionHandler 写数据(Writing Data Via a CompletionHandler)
你还可以使用 CompletionHandler
向 AsynchronousFileChannel
写入数据,该方法可以明确知道何时写入完成,而不像使用 Future
写入数据的方式那样需要自己进行检查。
下面是使用 CompletionHandler
将数据写入AsynchronousFileChannel
的示例:
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("test data".getBytes());
buffer.flip();
fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("bytes written: " + result);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("Write failed");
exc.printStackTrace();
}
});
和读取数据类似,当写入操作完成时,将调用 CompletionHandler
的 completed()
方法。如果由于某种原因写入操作失败,将调用 failed()
方法。
注意:ByteBuffer
是如何传递给 CompletionHandler
对象的方法的。
英文原文链接:http://tutorials.jenkov.com/java-nio/asynchronousfilechannel.html
------完------
推荐阅读:
Java NIO 简明教程 之 Java NIO 数据报通道(DatagramChannel)
Java NIO 简明教程 之 Java NIO 管道(Pipe)
Java NIO 简明教程 之 Java NIO vs. IO
Java NIO 简明教程 之 Java NIO 文件(Files)
参考资源:
https://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-asynchronousfilechannel.html