【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);