1. 程式人生 > >十、Nio之Selector

十、Nio之Selector

通過 Selector  選擇通道 一旦向 Selector 註冊了一或多個通道,就可以呼叫幾個過載的 select()方法。這些方法返回你所感興趣的事件(如連線、接受、讀或寫)已經準備就緒的那些通道。換句話說,如果你對―讀就緒‖的通道感興趣,select()方法會返回讀事件已經就緒的那些通道。 下面是 select()方法: ·  int select() ·  int select(long timeout) ·  int selectNow() select() 阻塞到至少有一個通道在你註冊的事件上就緒了。 select(long timeout) 和 select()一樣,除了最長會阻塞 timeout 毫秒(引數)。 selectNow() 不會阻塞,不管什麼通道就緒都立刻返回( 此方法執行非阻塞的選擇操作。如果自從前一次選擇操作後,沒有通道變成可選擇的,則此方法直接返回零。 )。
select()方法返回的 int 值表示有多少通道已經就緒。亦即,自上次呼叫 select()方法後有多少通道變成就緒狀態。 如果呼叫 select()方法,因為有一個通道變成就緒狀態,返回了 1,若再次呼叫 select()方法,如果另一個通道就緒 了,它會再次返回 1。如果對第一個就緒的 channel 沒有做任何操作,現在就有兩個就緒的通道,但在每次 select() 方法呼叫之間,只有一個通道就緒了。 selectedKeys() 一旦呼叫了select()方法, 並且返回值表明有一個或更多個通道就緒了, 然後可以通過呼叫selector的selectedKeys()方法,訪問“已選擇鍵集(selected key set)"中的就緒通道。如下所示:
1 Set selectedKeys = selector.selectedKeys(); 當像 Selector 註冊 Channel 時,Channel.register()方法會返回一個 SelectionKey 物件。這個物件代表了註冊到該 Selector 的通道。可以通過 SelectionKey 的 selectedKeySet()方法訪問這些物件。 可以遍歷這個已選擇的鍵集合來訪問就緒的通道。如下:
  1. Set selectedKeys = selector.selectedKeys();
  2. Iterator keyIterator = selectedKeys.iterator
    ();
  3. while(keyIterator.hasNext()){
  4. SelectionKey key = keyIterator.next();
  5. if(key.isAcceptable()){
  6. // a connection was accepted by a ServerSocketChannel.
  7. }elseif(key.isConnectable()){
  8. // a connection was established with a remote server.
  9. }elseif(key.isReadable()){
  10. // a channel is ready for reading
  11. }elseif(key.isWritable()){
  12. // a channel is ready for writing
  13. }
  14.    keyIterator.remove();
  15. }
這個迴圈遍歷已選擇鍵集中的每個鍵,並檢測各個鍵所對應的通道的就緒事件。 注意每次迭代末尾的 keyIterator.remove()呼叫。Selector 不會自己從已選擇鍵集中移除 SelectionKey 例項。必須在處理完通道時自己移除。下次該通道變成就緒時,Selector 會再次將其放入已選擇鍵集中。 SelectionKey.channel()方法返回的通道需要轉型成你要處理的型別,如 ServerSocketChannel 或 SocketChannel等。 wakeUp() 某個執行緒呼叫 select()方法後阻塞了,即使沒有通道已經就緒,也有辦法讓其從 select()方法返回。只要讓其它執行緒在第一個執行緒呼叫 select()方法的那個物件上呼叫 Selector.wakeup()方法即可。阻塞在 select()方法上的執行緒會立馬返回。 如果有其它執行緒呼叫了 wakeup()方法,但當前沒有執行緒阻塞在 select()方法上,下個呼叫 select()方法的執行緒會立即“醒來(wake up)"。 close() 用完 Selector 後呼叫其 close()方法會關閉該 Selector,且使註冊到該 Selector 上的所有 SelectionKey 例項無效。 通道本身並不會關閉。