1. 程式人生 > >【Java.NIO】API —— Channel介面

【Java.NIO】API —— Channel介面

Java NIO的通道類似流stream,但又有些不同:

  • 既可以從通道中讀取資料,又可以寫資料到通道。但流的讀寫通常是單向的
  • 通道可以非同步地讀寫
  • 通道中的資料總是先讀到一個Buffer,或者總是要從一個Buffer中寫入
  • Channel用於在位元組緩衝區和位於Channel另一側的實體(通常是一個檔案或套接字)之間有效的傳輸資料



java.nio.channels.Channel介面只聲明瞭兩個方法:

java.nio.channels
public interface Channel extends Closeable
  • close() - 關閉通道
  • isOpen() - 判斷通道是否開啟

通道在建立時被開啟,一旦關閉通道,就不能重新打開了。

一些子介面/子類

ReadableByteChannel和WritableByteChannel, 以及ByteChannel

兩個重要的子介面。

  • ReadabelByteChannel介面聲明瞭read(ByteBuffer dst)方法,把資料來源的資料讀入引數指定的ByteBuffer緩衝區中
  • WritableByteBuffer介面聲明瞭write(ByteBuffer src)方法,該方法把引數指定的ByteBuffer緩衝區中的資料寫到資料匯中


ByteChannel介面擴充套件了這兩個介面,因而同時支援讀寫操作。

分散和聚集 —— GatheringByteChannel和ScatteringByteChannel

  • ScatteringByteChannel介面擴充套件了ReadableByteChannel介面,允許分散地讀取資料。分散讀取資料是指單個讀取操作能填充多個緩衝區。ScatteringByteChannel介面聲明瞭read(ByteBuffer[] dsts)方法,該方法把從資料來源讀取的資料依次填充到引數指定的ByteBuffer數值中
  • GatheringByteChannel介面擴充套件了WritableByteChannel介面,允許集中地寫入資料。集中寫入資料是指單個寫操作能把多個緩衝區的資料寫到資料匯。GatheringByteChannel介面聲明瞭write(ByteBuffer[] srcs)方法,該方法依次把引數指定的ByteBuffer陣列的每個ByteBuffer中的資料寫入資料匯。
  • 分散讀取和集中寫資料能夠進一步提高輸入和輸出操作的速度。

Scattering Reads:


ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = {header, body};

channel.read(bufferArray);

注意buffer首先被插入到陣列,然後再將陣列作為channel.read()的輸入引數。read()方法按照buffer在陣列中的順序將從channel中讀取的資料寫到buffer中,當一個buffer被寫滿後,channel緊接著向另一個buffer中寫。

Scattering Reads在移動一下buffer前,必須填滿當前的buffer。這意味著它不適用於動態訊息。

Gathering Writes


ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = {header, body};

channel.write(bufferArray);

buffer陣列是write()方法的入參,write()方法會按照buffer在陣列中的順序,將資料寫入到channel,注意只有position和limit之間的資料才會被寫入。與scattering reads相反,gathering writes能較好地處理動態訊息。

FileChannel類

FileChannel類代表與檔案相連的通道。該類實現了ByteChannel, ScatteringByteChannel和GatheringByteChannel介面,支援讀寫操作,分散讀操作和集中寫操作。

FileChannel類沒有提供公開的構造方法,因此使用者不能使用new構造;不過,在FIleInputStream, FileOutputStream和RandomAccessFile類中提供了getChannel(0方法,該方法返回相應的FileChannel物件。

例如:

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

ByteBuffer buf = ByteBuffer.allocate(48);

int byteRead = inChannel.read(buf);
while (byteRead != -1){
    System.out.println("Read" + bytesRead);
    buf.flip();

    while(buf.hasRemaining()){
        System.out.println((char)buf.get());
    }

    buf.clear();
    bytesRead = inChannel.read(buf);
}

aFile.close();

SelectableChannel類

java.nio.channels
public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel

SelectableChannel是一種支援阻塞I/O和非阻塞I/O的通道

在非阻塞模式下,讀寫資料不會阻塞,並且SelectableChannel可以向Selector註冊讀就緒和寫就緒等事件。

Selector負責監控這些事件,等到事件發生時,比如發生了讀就緒事件,SelectableChannel就可以執行讀操作了。

SelectableChannel的主要方法如下:

  • confugureBlocking(boolean block) —— 當引數block為true時,表示設為阻塞模式;如果為false,表示設為非阻塞模式。預設情況下,採用阻塞模式,該方法返回物件本身的引用
  • isBlocking —— 判斷是否處於阻塞模式
  • register(Selector sel, int ops) —— 向Selector註冊事件
  • register(Selector sel, int ops, Object attachment) —— 向Selector註冊事件
  • 上兩個方法返回一個SelectionKey物件,用來跟蹤被註冊的事件。register()方法(第二個)還有一個Object型別的引數attachment,用於為SelectionKey關聯一個附件,當被註冊事件發生後,需要處理該事件時,可以從SelectionKey中獲得這個附件,該附件可用來包含與處理這個事件相關的資訊
SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
或者
MyHandler handler = new MyHandler();
SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, handler);
或者
SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
MyHandler handler = new MyHandler();
key.attach(handler);