Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

目录:
Java NIO 学习笔记(一)----概述,Channel/Buffer
Java NIO 学习笔记(二)----汇集和分散,通道到通道
Java NIO 学习笔记(三)----Selector
Java NIO 学习笔记(四)----文件通道和网络通道
Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
Java NIO 学习笔记(七)----NIO/IO 的对比和总结html

AsynchronousFileChannel 异步文件通道

在 Java 7 中,AsynchronousFileChannel 已添加到 Java NIO 中,它能够异步读取数据并将数据写入文件。先说明,异步和阻塞/非阻塞没有关系,下面简单介绍一下相关概念:java

  1. 阻塞是线程的一个状态,线程发起任务请求而后一直等,直到到任务完成再把结果返回,若是任务未完成当前线程会被挂起。
  2. 非阻塞是发起任务请求以后先立刻返回去作别的事,而后再时不时主动查看任务请求是否被完成。(轮询)
  3. 同步是同时只能有一个线程处理某个对象或者操做,例如一个线程占用了一个对象,其余线程若是要访问此对象,则须要等以前得线程操做完成返回,相关概念有同步方法、同步代码块、对象锁。
  4. 异步是若是完成任务时,遇到一个耗时操做(或者对象已经被别的线程占用了),不等待而是去作其余事情,也不主动查看是否完成,而是等耗时操做完成,发通知再叫线程回来处理结果,常见的例子就是 Ajax ,相关概念有回调函数等。

通常异步都是和非阻塞组合使用的。编程

建立 AsynchronousFileChannel

经过其静态方法 open() 建立 AsynchronousFileChannel ,下面是一个示例:网络

Path path = Paths.get("D:\\test\\input.txt");

AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);

// 做为对比,普通的 FileChannel 是这样打开的:
RandomAccessFile aFile = new RandomAccessFile("D:\\test\\input.txt", "rw");
FileChannel inChannel = aFile.getChannel();

open() 方法的第一个参数是指向 AsynchronousFileChannel 要关联的文件的 Path 实例。第二个参数是可选的,能够是一个或多个打开选项,这些选项告诉 AsynchronousFileChannel 在底层文件上执行什么操做。在这个例子中,咱们使用了 StandardOpenOption.READ,这意味着该文件将被打开以供阅读。多线程

读取数据

能够经过两种方式从 AsynchronousFileChannel 读取数据,都是调用 read() 方法之一完成的。dom

Future<Integer> read(ByteBuffer dst, long position);

<A> void read(ByteBuffer dst,
                                  long position,
                                  A attachment,
                                  CompletionHandler<Integer,? super A> handler);

经过 Future 读取数据

Future 是 Java 多线程方面的内容,后面我会再继续学习多线程的知识,这里先稍微了解,带过。
首先这个 Futrue 类有什么用?咱们知道多线程编程的时候,通常使用 Runable ,重写 run() 方法,而后调用线程对象的 start() 方法,重点就在 run() 方法的返回值是 void ,那若是咱们须要线程执行完成后返回结果怎么办,Future 就能够解决这个问题。异步

从 AsynchronousFileChannel 读取数据的第一种方法是调用 read() 方法,该方法返回 Future :async

Future<Integer> operation = fileChannel.read(buffer, 0);

此版本的 read() 方法将 ByteBuffer 做为第一个参数。 从 AsynchronousFileChannel 读取的数据被读入此 ByteBuffer ,第二个参数指定文件中要开始读取的字节位置。ide

即便读取操做还没有完成,read() 方法也会当即返回,能够经过调用 read() 方法返回的 Future 实例的 isDone() 方法来检查读取操做什么时候完成。这是一个示例:函数

Path path = Paths.get("D:\\test\\input.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);

ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
Future<Integer> operation = channel.read(buffer, position);

while (!operation.isDone()) ;

buffer.flip();
System.out.println(new String(buffer.array()));

此示例建立一个 AsynchronousFileChannel ,而后建立一个 ByteBuffer ,它做为参数传递给 read() 方法,而且位置为 0 ,在调用 read() 以后,循环调用 Future 的 isDone() 方法直到返回 true。 固然,这不是一个很是有效的 CPU 使用,可是这里须要等待读取操做完成。读取操做完成后,将数据读入 ByteBuffer 并输出。

经过 CompletionHandler 读取数据

从 AsynchronousFileChannel 读取数据的第二种方法是调用以 CompletionHandler 做为参数的 read() 方法版本,其第二个参数 position 可指定从什么位置开始读取。 如下是调用此 read() 方法的示例:

Path path = Paths.get("D:\\test\\input.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);

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: " + result);
        attachment.flip();
        System.out.println(new String(attachment.array()));
        attachment.clear();
    }

    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {
        System.out.println("failed");
    }
});

一旦读取操做完成,将调用 CompletionHandler 的 completed() 方法,completed() 方法的第一个参数是 Integer 类型,表示读取了多少字节,attachment 对应 read() 方法的第三个参数。 在这个例子中也是 ByteBuffer,数据也被读入其中。 能够自由选择要附加的对象。若是读取操做失败,则将调用 CompletionHandler 的 failed() 方法。

写入数据

和读取数据相似,也能够经过 AsynchronousFileChannel 对象的 2 种方法写入数据:

Future<Integer> write(ByteBuffer src, long position);

<A> void write(ByteBuffer src,
                                   long position,
                                   A attachment,
                                   CompletionHandler<Integer,? super A> handler);

具体的操做请参考读取数据,这里不展开了。

相关文章
相关标签/搜索