2012-03-30 56 views
8

我的Java NIO選擇使用select()所以塊來實現,直到任何這些發生:爪哇NIO選擇器選擇()返回0儘管通道是準備

  1. 註冊通道準備
  2. 它是wakeup()' ED
  3. 線程被中斷

由此,我對於該情況下的幾個假設,其中select()返回0:

  • 它一定是原因2或3
  • selectedKeys()應該返回一個空ResultSet
  • 我並不需要調用selectedKeys()並可以繼續下一個循環迭代,其中select()將被調用再

但是,我遇到的情況下select()返回0雖然有一個就緒通道。按照預期,selectedKeys()返回Set,1 SelectionKey

即使幾次調用select()也會一直返回0,直到通道被處理並且SelectionKey被移除。這種情況在一個無限循環爲select()不會阻止基本上結束了,但總是立即返回0

簡化代碼:

Selector selector = Selector.open(); 

SocketChannel channel; 

for (...) { // for each node 
    // Create and connect channels... 
    ... 

    channel.configureBlocking(false); 
    channel.register(selector, SelectionKey.OP_READ, someRelatedObject); 
} 

int ready; 
Set<SelectionKey> readyKeys; 
while (true) { 
    ready = selector.select(); 
    readyKeys = selector.selectedKeys(); 

    System.out.println("Ready channels: " + ready); 
    System.out.println("Selected channels: " + readyKeys.size()); 

    if (ready == 0) { 
    continue; 
    } 

    for (SelectionKey key : readyKeys) { 
    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 

    readyKeys.remove(key); 
    } 
} 

爲什麼select()返回0雖然有現成的渠道呢? 建議如何處理這個問題?

編輯:

更改此:

for (SelectionKey key : readyKeys) { 
    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 

    readyKeys.remove(key); 
    } 

這個

for (SelectionKey key : readyKeys) { 
    readyKeys.remove(key); 

    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 
    } 

解決了這個問題。在某些情況下,代碼將在remove()之前循環continuefor

編輯2:

我剛剛得知我的foreach循環超過設定的選擇鍵是壞的。 foreach使用集合的迭代器。迭代時直接修改集合(不通過迭代器的方法)可能會導致「任意的,不確定的」行爲。

所選鍵集可能會提供快速失敗迭代器。 快速失敗迭代器檢測到這種修改,並在下一次迭代時拋出ConcurrentModificationException。因此,修改foreach中的集合可能會導致不確定的行爲,或者可能導致異常 - 這取決於迭代器的實現。

解決方案:不要使用foreach。使用迭代器並通過iterator.remove()刪除密鑰。

Iterator<SelectionKey> iterator; 
SelectionKey key; 
while (true) { 
    // ... 

    iterator = selector.selectedKeys().iterator(); 
    while (iterator.hasNext()) { 
    key = iterator.next(); 
    iterator.remove(); 
    // ... 
    } 
} 

回答

7

select()返回已改變了按鍵的數量。因此,如果在select()呼叫之前鑰匙已經準備好,那麼它可能會返回0,但selectedKeys可能是非空的。

+2

可能是否一個密鑰準備好,並沒有被刪除? – riha 2012-03-30 09:17:37

1

正如您所指出的,這是因爲您沒有從選定的設置中刪除所選的鍵。

就像我們平時要處理的該組後刪除所有選擇鍵,你可以叫上設置clear()你的循環後,它可以是你想要的foreach或任何類型的循環:

Set<SelectionKey> readyKeys = selector.selectedKeys(); 
for (SelectionKey k : readyKeys) { 
    // Process k 
} 
readyKeys.clear(); 

這樣,您可以使用任何類型的循環(「常規」for或迭代器),並確保刪除所有就緒鍵,而不管您在for(包括有問題的continue)中執行的操作。根據iterator.remove()的內部做法,將clear()一次而不是幾個iterator.remove()(儘管這可能是微優化)可能更有效。

+0

不回答這個問題:「爲什麼select()返回0,儘管有一個ready channel?」 – EJP 2015-02-16 01:53:14

+0

@EJP你說得對,我已經糾正它。這將足以消除你的downvote? – Matthieu 2015-02-16 02:11:44

+0

通過'iterator.remove()'立即刪除每個'SelectionKey'有什麼優勢? – riha 2015-02-17 09:52:30