NIO技術-5-選擇器

NIO中的選擇器(Selector)的做用就是維護註冊到選擇器中的通道集合,每個通道與選擇器的關係封裝在選擇鍵(SelectionKey)中,實際上能夠認爲選擇器維護的是選擇鍵集合。建立Selector對象使用Selector.open()。java

Selector類主要維護三個集合:安全

1. Registered key set(已註冊鍵集合):調用Selector的keys()方法能夠獲取服務器

2. Selected key set(已選擇鍵集合):調用Selector的selectedKeys()方法獲取多線程

3. Cancelled key set(已取消鍵集合):已取消鍵集合是Selector對象的私有成員,外部沒法訪問併發

只有繼承了SelectableChannel的類才能註冊到選擇器中,而且只有非阻塞模式的通道才能註冊到選擇器,以下代碼:socket

//建立一個套接字服務器,並註冊到選擇器  
//建立選擇器  
Selector selector = Selector.open();  
//建立Socket服務器通道  
ServerSocketChannel ssc = ServerSocketChannel.open();  
//綁定65535端口  
ssc.socket().bind(new InetSocketAddress(65535));  
//設置通道爲非阻塞模式  
ssc.configureBlocking(false);  
//將通道註冊到選擇器,指定通道興趣是等待接收鏈接  
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);

實際使用中,通常使用while循環輪詢獲取註冊到選擇器中通道選擇器的操做,以下代碼:ide

while (true) {  
    int n = selector.select();  
    if (n > 0) {  
        Iterator<SelectionKey> iter = selector.selectedKeys().iterator();  
        while (iter.hasNext()) {  
            SelectionKey keyy = iter.next();  
            iter.remove();  
            // ......  
        }  
    }  
}

select()方法的做用是選擇註冊到選擇器中通道感興趣的鍵,此方法是阻塞的,直到有感興趣的事件發生;還可使用select(10000)方法設置選擇鍵的超時時間,單位是Millisecond;還可使用selectNow(),此方法是非阻塞,若沒有通道就緒會當即返回0。線程

不須要使用Selector時,調用close()方法能夠關閉選擇器,關閉選擇器後,全部註冊到其中的選擇鍵會被設置爲無效狀態。設計

關閉選擇器後,試圖調用它的方法會拋出ClosedSelectorException,這是一個非運行時異常,全部在使用時有必要檢查選擇器是否打開,使用isOpen()方法。rest

NIO中定義了4中可選擇操做:OP_READ(讀)、OP_WRITE(寫)、OP_CONNECT(鏈接)、OP_ACCEPT(接受),這些常量在SelectionKey中定義。可使用通道的validOps()獲取某個通道上支持的操做集合。

同一個通道能夠被屢次註冊到同一個選擇器中,沒有異常錯誤,也沒有特別的效果,我的以爲這個地方設計的不嚴謹;實際使用中應避免同一個通道屢次註冊到一個選擇器中,雖然對實際功能沒有影響,可是爲了總體思路清晰最好不要這麼作。

註冊一個通道時能夠在這個選擇鍵上設置一個Object對象,好比在接受鏈接操做中設置,在讀操做中獲取,以下代碼:

SelectionKey keyy = iter.next();  
iter.remove();  
if(keyy.isAcceptable()){  
    ServerSocketChannel channel = (ServerSocketChannel) keyy.channel();  
    SocketChannel sc = channel.accept();  
    sc.register(selector, SelectionKey.OP_READ, "hello");  
}  
if(keyy.isReadable()){  
    //attach的值爲"hello"  
    Object attach = keyy.attachment();  
    SocketChannel sc = (SocketChannel) keyy.channel();  
}

SelectionKey(選擇鍵對象)提供了下面方法:

channel():獲取關聯的通道(SelectableChannel)
selector():獲取關聯的選擇器(Selector)
isValid():驗證維護選擇器與通道關係的SelectionKey是否有效
cancel():取消鍵,調用一個已取消的鍵的方法將拋出CancelledKeyException
interestOps():獲取這個key的興趣(可選擇操做)集合
interestOps(int ops):設置這個key的興趣
readyOps():返回這個key準備好的操做集合(興趣集合)
isReadable():檢查選擇鍵的興趣是否爲可讀,其實是通道的興趣,選擇鍵維護通道與選擇器關係
isWritable():檢查選擇鍵的興趣是否爲可寫
isConnectable():檢查選擇鍵的興趣是否爲鏈接
isAcceptable():檢查選擇鍵的興趣是否爲接受
attach(Object ob):設置一個Object數據到此key上
attachment():獲取設置的Object數據

能夠設置null來清楚SelectionKey的附件對象,若是SelectionKey的存在週期很長,可是附件對象不須要存在很長,必定要再使用完後即時清理附件對象,不然附件對象不能被GC回收,可能會發生內存泄露。

中止選擇過程可使用Selector的wakeup()方法,此方法能夠安全的退出select()阻塞,

選擇器對象(Selector)是線程安全的,在多線程併發訪問不會有問題。

Selector(選擇器)提供了下面方法:

open():打開一個選擇器
isOpen():檢查一個選擇器實例是否打開
provider():返回一個SelectorProvider
keys():返回註冊鍵集合
selectedKeys():返回已選擇鍵集合
selectNow():馬上執行選擇,非阻塞,若沒有已準備好的通道則當即返回0
select(long timeout):執行選擇,超過指定毫秒數則返回
select():執行選擇,會一直阻塞直到有準備就緒的通道
wakeup():中止選擇
close():關閉選擇器

使用Selector類可能會拋出的錯誤:

IllegalBlockingModeException:註冊一個阻塞狀態的通道拋出此異常
ClosedChannelException:註冊一個已關閉的通道拋出此異常
ClosedSelectorException:調用一個已關閉的選擇器的方法拋出此異常
UnsupportedOperationException:已註冊的鍵集合是隻讀,修改它會拋出此異常
相關文章
相關標籤/搜索