在 Java 7,AsynchronousFileChannel 被添加到了 Java NIO 中. 使用 AsynchronousFileChannel 可以实现异步地读取和写入文件数据.
创建一个 AsynchronousFileChannel
我们可以使用 AsynchronousFileChannel 提供的静态方法 open() 创建它. 示例代码如下:
- Path path = Paths.get("data/test.xml");
- AsynchronousFileChannel fileChannel =
- AsynchronousFileChannel.open(path, StandardOpenOption.READ);
第一个参数是一个 PATH 的对像实例, 它指向了那个与 AsynchronousFileChannel 相关联的文件.
第二个参数是一个或多个操作选项, 它决定了 AsynchronousFileChannel 将对目标文件做何种操作. 示例代码中我们使用了 StandardOpenOption.READ , 它表明我们将要对目标文件进行读操作.
读取数据
AsynchronousFileChannel 提供了两种读取数据的方式, 都是调用它本身的 read() 方法. 下面将对两种方式进行介绍.
使用 Futrue 读取数据
第一种反式是调用 AsynchronousFileChannel 的 read() 方法, 该方法反回一个 Future 类型的对象.
Future operation = fileChannelread(buffer, 0);
第一个参数是 ByteBuffer, 从 AsynchronousFileChannel 中读取的数据先写入这个 ByteBuffer .
第二个参数表示从文件读取数据的开始位置.
此 read() 方法会立即返回, 即使整个读的过程还没有完全结束. 我们可以通过 operation.isDone() 来检查读取是否完成. 这里的 operation 是上面调用 read() 方法返回的 Future 类型的实例. 下面是一段详细的代码示例:
- 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 对象, 然后调用它的 read() 方法返回一个 Future. 其中 read() 方法需要两个参数, 一个是 ByteBuffer, 另一个是读取文件的开始位置. 然后通过循环调用 isDone() 方法检测读取过程是否完成, 完成后 isDone() 方法将返回 true. 尽管这样让 CPU 空转了一会, 但是我们还是应该等读取操作完成后再进行后续的步骤.
一旦读取完成, 数据被存储到 ByteBuffer, 然后将数据转化为字符串既而输出.
使用 CompletionHandler 读取数据
第二种读取数据的方式是调用 AsynchronousFileChannel 的另一个重载 read() 方法, 改方法需要一个 CompletionHandler 作为参数. 下面是代码示例:
- 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 的 complete() 方法将会被调用. 它的第一个参数是个 Integer 类型, 表示读取的字节数. 第二个参数 attachment 是 ByteBuffer 类型的, 用来存储读取的数据. 它其实就是由 read() 方法的第三个参数. 当前示例中, 我们选用 ByteBuffer 来存储数据, 其实我们也可以选用其他的类型.
读取失败的时候, CompletionHandler 的 failed() 方法会被调用.
写入数据
就像读取一样, 我们同样有两种方式向 AsynchronousFileChannel 写入数据. 我们可以调用它的 2 个重载的 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("test data".getBytes());
- buffer.flip();
- Future<Integer> operation = fileChannel.write(buffer, position);
- buffer.clear();
- while(!operation.isDone());
- System.out.println("Write done");
首先实例化一个写入模式的 AsynchronousFileChannel, 然后创建一个 ByteBuffer 并写入一些数据. 再然后将数据写入文件. 最后, 检查返回的 Future, 看是否写入完成.
注意, 写入目标文件要提前创建好, 如果它不存在的话, writh() 方法会抛出一个 java.nio.file.NoSuchFileException.
我们可以用以下方式来解决这一问题:
- if(!Files.exists(path)){
- Files.createFile(path);
- }
使用 CompletionHandler 写入数据
我们也可以使用 CompletionHandler 代替 Future 向 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() 方法.
要留意 CompletionHandler 的方法的参数 attachemnt 是怎么使用的.
最后
私信回复 资料 领取一线大厂 Java 面试题总结 + 阿里巴巴泰山手册 + 各知识点学习思维导 + 一份 300 页 PDF 文档的 Java 核心知识点总结!
这些资料的内容都是面试时面试官必问的知识点, 篇章包括了很多知识点, 其中包括了有基础知识, Java 集合, JVM, 多线程并发, spring 原理, 微服务, Netty 与 RPC ,Kafka, 日记, 设计模式, Java 算法, 数据库, Zookeeper, 分布式缓存, 数据结构等等.
来源: http://www.bubuko.com/infodetail-3654006.html