Channel 是 Java NIO 核心抽象,代表與 I/O 服務(檔案、網路套接字)的雙向連接管道。本文詳細講解通道概念、Channel 介面定義、四大核心通道類型(FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel)的建立方式、ByteBuffer 配合讀寫模式、單向/雙向通道區別、通道關閉與中斷處理,並提供完整的通道間資料拷貝範例。
Channel 負責傳輸,Buffer 負責存儲public interface Channel extends Closeable {
boolean isOpen(); // 判斷通道是否開啟
void close() throws IOException; // 關閉通道
}
簡潔而強大,所有通道實現類均繼承此介面。
無法直接 new,必須從已開啟的檔案流取得:
RandomAccessFile raf = new RandomAccessFile("somefile", "rw");
FileChannel fc = raf.getChannel(); // 可讀可寫
// 或從輸入/輸出流
FileInputStream fis = new FileInputStream("in.txt");
FileChannel fcIn = fis.getChannel(); // 僅可讀
FileOutputStream fos = new FileOutputStream("out.txt");
FileChannel fcOut = fos.getChannel(); // 僅可寫
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("somehost", 8080));
// 或非阻塞模式
sc.configureBlocking(false);
sc.connect(new InetSocketAddress("host", port));
// 需輪詢 sc.finishConnect()
ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.socket().bind(new InetSocketAddress(8080)); // 或 ssc.bind(new InetSocketAddress(8080)); // JDK 7+ // 非阻塞模式 ssc.configureBlocking(false);
DatagramChannel dc = DatagramChannel.open(); dc.socket().bind(new InetSocketAddress(9090)); // 非阻塞 dc.configureBlocking(false);
ReadableByteChannel:read(ByteBuffer dst)WritableByteChannel:write(ByteBuffer src)ByteChannel 同時繼承兩者 → 雙向通道FileChannel 實作 ByteChannel(雙向)SocketChannel 實作 ByteChannel(雙向)ServerSocketChannel 不直接讀寫,透過 accept() 產生 SocketChannel// 讀取:通道 -> Buffer
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); // 直接緩衝區,零拷貝優勢
while (channel.read(buffer) != -1) {
buffer.flip(); // 切換讀模式
// 處理 buffer 中資料
buffer.clear(); // 或 compact() 保留未讀資料
}
// 寫入:Buffer -> 通道
buffer.put(data);
buffer.flip();
while (buffer.hasRemaining()) {
channel.write(buffer); // 可能部分寫入,需循環
}
buffer.clear();
package nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
public class ChannelCopyDemo {
public static void main(String[] args) throws IOException {
// 系統標準輸入/輸出包裝為通道
ReadableByteChannel source = Channels.newChannel(System.in);
WritableByteChannel dest = Channels.newChannel(System.out);
channelCopy1(source, dest); // 或 channelCopy2
source.close();
dest.close();
}
// 版本 1:使用 compact() 保留未寫完資料
private static void channelCopy1(ReadableByteChannel src, WritableByteChannel dest) throws IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while (src.read(buffer) != -1) {
buffer.flip();
// 不保證一次寫完,需配合 compact()
dest.write(buffer);
buffer.compact(); // 未讀資料移到開頭,position 更新,準備下次讀取
}
// EOF 後處理殘留資料
buffer.flip();
while (buffer.hasRemaining()) dest.write(buffer);
}
// 版本 2:使用 clear(),每次確保寫完再清空
private static void channelCopy2(ReadableByteChannel src, WritableByteChannel dest) throws IOException {
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while (src.read(buffer) != -1) {
buffer.flip();
while (buffer.hasRemaining()) dest.write(buffer); // 確保寫完
buffer.clear(); // 完全清空,position=0, limit=capacity
}
// EOF 後 buffer 已空,無需額外處理
}
}
close():釋放底層檔案描述符/Socket,後續讀寫拋 ClosedChannelExceptionisOpen():檢查通道狀態InterruptibleChannel 的通道(如 FileChannel、SocketChannel):執行緒被中斷時,通道自動關閉,執行緒收到 ClosedByInterruptExceptiontry-with-resources 自動關閉| 特性 | 直接緩衝區 allocateDirect | 堆緩衝區 allocate |
|---|---|---|
| 記憶體位置 | 堆外 | JVM 堆內 |
| 分配/釋放成本 | 較高 | 較低 |
| I/O 效能 | 零拷貝,極快 | 需堆->堆外拷貝 |
| GC 壓力 | 不佔堆,減輕 GC | 佔堆,增加 GC |
| 適用場景 | 大量、長壽命、高頻 I/O | 小型、短壽命、低頻 I/O |