17 Java NIO AsynchronousFileChannel-翻譯

王金龍發表於2017-12-06

在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方法的引數。

相關文章