在 Java 7 中,AsynchronousFileChannel 已添加到 Java NIO 中,它可以異步讀取資料并将資料寫入檔案
目錄:
Java NIO 學習筆記(一)----概述,Channel/Buffer
Java NIO 學習筆記(二)----聚集和分散,通道到通道
Java NIO 學習筆記(三)----Selector
Java NIO 學習筆記(四)----檔案通道和網絡通道
Java NIO 學習筆記(五)----路徑、檔案和管道 Path/Files/Pipe
Java NIO 學習筆記(六)----異步檔案通道 AsynchronousFileChannel
Java NIO 學習筆記(七)----NIO/IO 的對比和總結
AsynchronousFileChannel 異步檔案通道
在 Java 7 中,AsynchronousFileChannel 已添加到 Java NIO 中,它可以異步讀取資料并将資料寫入檔案。先說明,異步和阻塞/非阻塞沒有關系,下面簡單介紹一下相關概念:
- 阻塞是線程的一個狀态,線程發起任務請求然後一直等,直到到任務完成再把結果傳回,如果任務未完成目前線程會被挂起。
- 非阻塞是發起任務請求之後先馬上傳回去做别的事,然後再時不時主動檢視任務請求是否被完成。(輪詢)
- 同步是同時隻能有一個線程處理某個對象或者操作,例如一個線程占用了一個對象,其他線程如果要通路此對象,則需要等之前得線程操作完成傳回,相關概念有同步方法、同步代碼塊、對象鎖。
- 異步是如果完成任務時,遇到一個耗時操作(或者對象已經被别的線程占用了),不等待而是去做其他事情,也不主動檢視是否完成,而是等耗時操作完成,發通知再叫線程回來處理結果,常見的例子就是 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() 方法之一完成的。
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 :
Future<Integer> operation = fileChannel.read(buffer, 0);
此版本的 read() 方法将 ByteBuffer 作為第一個參數。 從 AsynchronousFileChannel 讀取的資料被讀入此 ByteBuffer ,第二個參數指定檔案中要開始讀取的位元組位置。
即使讀取操作尚未完成,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);
具體的操作請參考讀取資料,這裡不展開了。
如果覺得本文有所幫助,歡迎點【推薦】!文章錯誤之處煩請留言。
轉載說明:轉載後必須在文章開頭明顯地給出作者和原文連結;引用必須注明出處;需要二次修改釋出請聯系作者征得同意。