Selector SelectionKey
NIO能通過單個線程管理多個I/O通道,主要就是通過選擇器Selector來實現的。
public abstract class Selector {protected Selector() { }public static Selector open() throws IOException {return SelectorProvider.provider().openSelector();}public abstract boolean isOpen();public abstract SelectorProvider provider();public abstract Set<SelectionKey> keys();public abstract Set<SelectionKey> selectedKeys();public abstract int selectNow() throws IOException;public abstract int select(long timeout)throws IOException;public abstract int select() throws IOException;public abstract Selector wakeup();public abstract void close() throws IOException; }當我們調用
channel.register(selector, SelectionKey.OP_READ, buffer);方法時,會將通道channel注冊到選擇器selector上,同時會返回一個SelectionKey選擇鍵對象,這個鍵對象標識了通道和選擇器之間的注冊關系。選擇鍵會記住您關心的通道。它們也會追蹤對應的通道是否已經就緒。當您調用一個選擇器對象的select( )方法時,相關的鍵建會被更新,用來檢查所有被注冊到該選擇器的通道。您可以獲取一個就緒鍵的集合,從而找到當時已經就緒的通道。通過遍歷這些鍵,您可以選擇出每個從上次您調用select( )開始直到現在,已經就緒的通道。
public abstract class SelectionKey {//以下四個常量是定義的通道的四種操作public static final int OP_READ = 1;public static final int OP_WRITE = 4;public static final int OP_CONNECT = 8;public static final int OP_ACCEPT = 16;//可以針對指定的通道附加一個對象 private volatile Object attachment = null;// 獲得通道對象public abstract SelectableChannel channel();// 獲得通道所注冊的選擇器public abstract Selector selector();// 驗證通道和選擇器之間的注冊關系是否還生效public abstract boolean isValid();//將通道放入選擇器的已注銷集合中public abstract void cancel();//獲得該通道所感興趣的操作public abstract int interestOps();//設置通道感興趣的操作public abstract SelectionKey interestOps(int paramInt);//獲得通道已準備好的操作public abstract int readyOps();public final boolean isReadable() {return ((readyOps() & 0x1) != 0);}public final boolean isWritable() {return ((readyOps() & 0x4) != 0);}public final boolean isConnectable() {return ((readyOps() & 0x8) != 0);}public final boolean isAcceptable() {return ((readyOps() & 0x10) != 0);}public final Object attach(Object paramObject) {Object localObject = this.attachment;this.attachment = paramObject;return localObject;}public final Object attachment() {return this.attachment;} }一個SelectionKey選擇鍵對象包含兩個以整數形式進行編碼的比特掩碼:一個用于指示那些通道/選擇器組合體所關心的操作(instrest集合), 另一個表示通道準備好要執行的操作(ready集合)??梢酝ㄟ^調用鍵對象的interestOps( )方法來獲取,這個interset集合永遠不會被選擇器改變, 但可以通過調用選擇鍵interestOps(int value )方法并傳入一個新的比特掩碼參數來改變它。ready集合是interest集合的子集,可以通過調用鍵的readyOps( )方法來獲取相關的通道的已經就緒的操作并且表示了interest集合中從上次調用select( )以來已經就緒的那些操作。
每個選擇器selector中會包含三種鍵的集合,也可以認為是三種狀態通道的集合,比較通道和選擇鍵是一一對應的:
- (1) 注冊鍵的集合,通過register函數進行注冊的通道這個集合可以通過Keys()方法返回
- (2) 已選擇鍵的集合,這個是注冊鍵集合的子集,可以通過selectedKeys()方法返回,該集合中的通道都是準備好執行相應操作的通道
- (3) 已取消鍵的集合,這個集合包含了cancel()方法被調用過的鍵(這個鍵已經被無效化),但它們還沒有被注銷。這個集合是選擇器對象的私有成員,因而無法直接訪問。
當我們調用選擇器的select()方法時,會影響上述三種鍵的集合,調用之后會發生如下過程:
-
1.已取消的鍵的集合將會被檢查。如果它是非空的,每個已取消的鍵的集合中的鍵將從另外兩個集合中移除,并且相關的通道將被注銷。 這個步驟結束后,已取消的鍵的集合將是空的。
-
2.已注冊的鍵的集合中的鍵的interest集合將被檢查。在這個步驟中的檢查執行過后,對interest集合的改動不會影響剩余的檢查過程。一旦就緒條件被定下來,底層操作系統將會進行查詢,以確定每個通道所關心的操作的真實就緒狀態。依賴于特定的select( )方法調用,如果沒有通道已經準備好,線程可能會在這時阻塞,通常會有一個超時值。直到系統調用完成為止,這個過程可能會使得調用線程睡眠一段時間,然后當前每個通道的就緒狀態將確定下來。對于那些還沒準備好的通道將不會執行任何的操作。對于那些操作系統指示至少已經準備好interest集合中的一種操作的通道,將執行以下兩種操作中的一種:
a.如果通道的鍵還沒有處于已選擇的鍵的集合中,那么鍵的ready集合將被清空,然后表示操作系統發現的當前通道已經準備好的操作的比特掩碼將被設置。
b.否則,也就是鍵在已選擇的鍵的集合中。鍵的ready集合將被表示操作系統發現的當前已經準備好的操作的比特掩碼更新。所有之前的已經不再是就緒狀態的操作不會被清除。事實上,所有的比特位都不會被清理。由操作系統決定的ready集合是與之前的ready集合按位分離的,一旦鍵被放置于選擇器的已選擇的鍵的集合中,它的ready集合將是累積的。比特位只會被設置,不會被清理。
-
3.步驟2可能會花費很長時間,特別是所激發的線程處于休眠狀態時。與該選擇器相關的鍵可能會同時被取消。當步驟2結束時,步驟1將重新執行,以完成任意一個在選擇進行的過程中,鍵已經被取消的通道的注銷。
-
4.select操作返回的值是ready集合在步驟2中被修改的鍵的數量,而不是已選擇的鍵的集合中的通道的總數。返回值不是已準備好的通道的總數,而是從上一個select()調用之后進入就緒狀態的通道的數量。之前的調用中就緒的,并且在本次調用中仍然就緒的通道不會被計入,而那些在前一次調用中已經就緒但已經不再處于就緒狀態的通道也不會被計入。這些通道可能仍然在已選擇的鍵的集合中,但不會被計入返回值中。返回值可能是0。
原文鏈接:https://blog.csdn.net/u011784767/article/details/74750153
總結
以上是生活随笔為你收集整理的Selector SelectionKey的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jackson 注解 -- 类型转换(序
- 下一篇: 分享一篇关于饿了么的需求文档