首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java NIO 通道 Channel 基礎入門:檔案與網路通道建立、讀寫與關閉全流程

     編輯:Java 相關知識

Channel 是 Java NIO 核心抽象,代表與 I/O 服務(檔案、網路套接字)的雙向連接管道。本文詳細講解通道概念、Channel 介面定義、四大核心通道類型(FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel)的建立方式、ByteBuffer 配合讀寫模式、單向/雙向通道區別、通道關閉與中斷處理,並提供完整的通道間資料拷貝範例。

一、什麼是通道

  • 通道是存取 I/O 服務的導管,將緩衝區資料傳輸至實體(檔案、Socket)或從實體讀入緩衝區
  • 通道與緩衝區互動:Channel 負責傳輸,Buffer 負責存儲
  • 最小開銷存取作業系統原生 I/O 服務

二、Channel 介面定義

public interface Channel extends Closeable {
    boolean isOpen();           // 判斷通道是否開啟
    void close() throws IOException;  // 關閉通道
}

簡潔而強大,所有通道實現類均繼承此介面。

三、四大核心通道建立方式

1. FileChannel(檔案通道)

無法直接 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(); // 僅可寫

2. SocketChannel(客戶端 TCP 通道)

SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("somehost", 8080));
// 或非阻塞模式
sc.configureBlocking(false);
sc.connect(new InetSocketAddress("host", port));
// 需輪詢 sc.finishConnect()

3. ServerSocketChannel(伺服端 TCP 通道)

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(8080));
// 或
ssc.bind(new InetSocketAddress(8080));  // JDK 7+
// 非阻塞模式
ssc.configureBlocking(false);

4. DatagramChannel(UDP 通道)

DatagramChannel dc = DatagramChannel.open();
dc.socket().bind(new InetSocketAddress(9090));
// 非阻塞
dc.configureBlocking(false);

四、通道讀寫與 ByteBuffer 協作

可讀/可寫介面

  • 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,後續讀寫拋 ClosedChannelException
  • isOpen():檢查通道狀態
  • 實作 InterruptibleChannel 的通道(如 FileChannel、SocketChannel):執行緒被中斷時,通道自動關閉,執行緒收到 ClosedByInterruptException
  • 良好習慣:try-with-resources 自動關閉

七、直接緩衝區 vs 堆緩衝區

八、總結

NIO 通道與緩衝區模式是高效能 I/O 基石。掌握四大通道建立、ByteBuffer 翻轉模式(flip/compact/clear)、直接緩衝區優勢、關閉與中斷語義,是構建高併發網路伺服器、高效檔案處理系統的前提。下一篇將深入 Scatter/Gather 分散聚集讀寫 進階技術。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性直接緩衝區 allocateDirect堆緩衝區 allocate
記憶體位置堆外JVM 堆內
分配/釋放成本較高較低
I/O 效能零拷貝,極快需堆->堆外拷貝
GC 壓力不佔堆,減輕 GC佔堆,增加 GC
適用場景大量、長壽命、高頻 I/O小型、短壽命、低頻 I/O