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