Java NIO AsynchronousFileChannel

时间:2020-01-09 10:36:13  来源:igfitidea点击:

在Java 7中," AsynchronousFileChannel"已添加到Java NIO。 AsynchronousFileChannel使得异步读取数据和向文件写入数据成为可能。本教程将说明如何使用AsynchronousFileChannel

创建一个AsynchronousFileChannel

我们可以通过静态方法open()创建一个AsynchronousFileChannel。这是创建AsynchronousFileChannel的示例:

Path path = Paths.get("data/test.xml");

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

open()方法的第一个参数是一个Path实例,该实例指向与AsynchronousFileChannel关联的文件。

第二个参数是一个或者多个打开的选项,它们告诉" AsynchronousFileChannel"要在基础文件上执行哪些操作。在此示例中,我们使用了" StandardOpenOption.READ",这意味着将打开文件进行读取。

读取数据

我们可以通过两种方式从" AsynchronousFileChannel"中读取数据。每种读取数据的方法都调用" AsynchronousFileChannel"的" read()"方法之一。以下各节将介绍这两种读取数据的方法。

通过未来读取数据

从" AsynchronousFileChannel"读取数据的第一种方法是调用" read()"方法,该方法返回" Future"。这是调用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。在调用" read()"之后,该示例循环运行,直到" isDone( )返回的" Future"的方法返回true。当然,这不是对CPU的非常有效的使用,但是我们需要以某种方式等待读取操作完成。

一旦读取操作完成,数据将被读入到" ByteBuffer"中,然后被读入一个String中,并被打印到" System.out"中。

通过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) {

    }
});

一旦读取操作完成,CompletionHandlercompleted()方法将被调用。作为" completed()"方法的参数,将传递" Integer"(告诉读取了多少字节),并将"附件"传递给" read()"方法。 "附件"是read()方法的第三个参数。在这种情况下,也就是" ByteBuffer",数据也被读入其中。我们可以自由选择要添加的对象。

如果读取操作失败,则将改为调用CompletionHandler的failed()方法。

写数据

就像读取一样,我们可以通过两种方式将数据写入" AsynchronousFileChannel"。每种写数据的方法都调用" AsynchronousFileChannel"的" write()"方法之一。以下各节将介绍这两种写入数据的方法。

通过未来写入数据

AsynchronousFileChannel也使我们能够异步写入数据。这是完整的JavaAsynchronousFileChannel编写示例:

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以查看写操作何时完成。

请注意,此代码生效之前,该文件必须已经存在。如果文件不存在,则write()方法将抛出java.nio.file.NoSuchFileException`。

我们可以使用以下代码确保Path指向的文件存在:

if(!Files.exists(path)){
    Files.createFile(path);
}

通过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"的方法。