17 Java NIO AsynchronousFileChannel-翻译

1,060 阅读4分钟

在Java 7中,向Java NIO中添加了AsynchronizedFileChannel。这个类使得文件的读写支持异步。这个教程会介绍如何使用AsynchronizedFileChannel。

创建AsynchronousFileChannel

创建AsynchronousFileChannel可以通过静态方法open()来实现。下面是创建AsynchronousFileChannel的一个例子:

Path path = Paths.get("data/test.cml");
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path,StandOpenOPtion.READ;


open方法的第一个参数是Path实例,这个实例指向了AsynchronousFileChannel所关联的文件。

open方法的第二个参数是一个或多个打开选项,这告诉AsynchronousFileChannel需要如何操作将要处理的文件。在这个例子中,我们使用的StandardOpenOption.READ说明这个文件是用来只读的。

读取数据

从AsynchronousFileChannel中读取数据有两种方式。每种方法都是调用AsynchronousFileChannel的read()方法。这两种方法将在以下小节进行介绍:

通过Future读取数据

第一种方式是通过调用AsynchronousFileChannel的read方法并返回一个Future对象。如下所示:

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

read()方法将buffer作为第一个参数。从AsynchronousFileChannel中读取的数据会被放入到ByteBuffer中。第二个参数是字节的开始读取位置。

read()方法会立刻返回,即使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()方法,同时position的值为0。在调用完read方法之后,这个例子会一直循环直接返回的Future对象的isDone()返回true。当然,这种方式使用cpu的效率并不是很高,但是你必须以某种方式等待操作完成。

一旦读操作完成,数据将会写入到ByteBuffer中,然后放入到String中,并通过System.out打印出来。

通过CompletionHandler读取数据

第二个通过read方法读取数据的方式是传递一个CompletionHandler作为参数。下面是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) {

    }
});

一旦读操作结束,completed方法将会被调用。completed方法的Integer参数表明已经读取的字节数,attachment也会作为参数传递。attachment也是read方法的第三个参数。在这种情况下,数据也会被读入到这个ByteBuffer中。你可以自由选择attachment。

如果读操作失败了,会调用failed方法。

写数据

跟读操作一亲,写操作也有两种方式,每种方式都是调用write方法。这两种方法都会在下面的小节中进行介绍。

通过Futrue写入数据

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对象的write方法是否已经完成。

注意到,要使这段代码正常工作需要保证文件已经存在。如果文件不存在,则会抛出java.nio.file.NoSuchFileException。

你可以通过以下方式保证文件一定存在:

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

通过CompletionHandler写入数据

向AsynchronousFileChannel写入数据也可以通过CompletionHandler来代替Future的方式。下面是一个使用AsynchronousFileChannel的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("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是如何被用来作为attachment的-这个对象是作为CompletionHandler方法的参数。