非同步 IO 核心類別
Java 7 引入 NIO.2,提供真正的非同步 IO 能力,核心通道類別:
AsynchronousFileChannel:檔案非同步讀寫
AsynchronousSocketChannel:客戶端非同步 Socket
AsynchronousServerSocketChannel:伺服器端非同步接受連線
兩種完成通知模式
| 模式 | 介面 | 特點 | 適用場景 |
| Future 模式 | Future<V> | 主動輪詢/阻塞等待結果 | 需同步等待結果、可中斷、可超時控制 |
| Callback 模式 | CompletionHandler<V,A> | 完成時自動回調 | 高併發、不想阻塞執行緒、事件驅動架構 |
Future 模式實作:大檔案非同步讀取
try (AsynchronousFileChannel channel =
AsynchronousFileChannel.open(Paths.get("large-file.dat"))) {
ByteBuffer buffer = ByteBuffer.allocateDirect(100 * 1024 * 1024); // 100MB 直接記憶體
Future<Integer> result = channel.read(buffer, 0); // 位置 0 開始讀
// 執行其他有用工作
while (!result.isDone()) {
System.out.println("讀取中,可處理其他任務...");
// doOtherWork();
}
System.out.println("實際讀取位元組數: " + result.get()); // 阻塞直到完成
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
Callback 模式實作:非同步寫入
byte[] data = {2, 3, 5, 7, 11, 13, 17, 19, 23};
ByteBuffer buffer = ByteBuffer.wrap(data);
CompletionHandler<Integer, Object> handler = new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
System.out.println("寫入完成,位元組數: " + result);
// 可在此鏈接下一個非同步操作
}
@Override
public void failed(Throwable exc, Object attachment) {
System.err.println("寫入失敗: " + exc.getMessage());
}
};
try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(
Paths.get("primes.txt"),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
channel.write(buffer, 0, null, handler); // attachment 可傳遞上下文物件
// 主執行緒需等待,否則程式結束通道關閉
Thread.sleep(10000);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
執行緒池與資源管理
- 預設使用 JVM 共享執行緒池(
ForkJoinPool.commonPool())
- 可自訂執行緒池:
AsynchronousFileChannel.open(path, executor)
- try-with-resources 確保通道關閉,但需注意:關閉通道會取消未完成操作
CompletionHandler 進階方法
| 方法 | 用途 |
completed(result, attachment) | 成功回調 |
failed(exc, attachment) | 失敗回調 |
awaitUninterruptibly() | 阻塞等待且不可中斷 |
awaitUntil(Date deadline) | 限時等待 |
對比:非同步 vs 多路複用
- 非同步 IO (AIO):核心完成 IO 後通知應用,適用於高併發、長耗時操作
- 多路複用 (NIO):
Selector 輪詢通道就緒狀態,單執行緒管理多連線,適用於大量短連線
- Java 兩者共存,可根據業務特性混合使用
實務建議
- 大檔案順序讀寫:
Files.copy() 或 FileChannel.transferTo() 零拷貝更高效
- 網路服務:Netty 等框架封裝了 NIO/AIO,生產環境優先使用成熟框架
- 直接記憶體 (
allocateDirect) 減少堆外拷貝,但分配/回收成本較高,需池化
- 錯誤處理:
ExecutionException 包裝真實異常,需 getCause() 獲取