Java NIO問題レコード:チャネルの準備ができているときに、select()がまだ0を返しますか?

問題

今日、NIOの背景をデバッグすると、卵の痛みの問題が見つかりました。デバッグ後、問題の流れは次のように説明されます。

クライアントはサーバーにメッセージを送信し、サーバーはIO多重化を使用して入力を処理します。つまり、1つのセレクターが複数のチャネルをリッスンします。最初のメッセージは正常に受信されます。2番目のメッセージが送信されると、select()はすぐに0を返し、無限ループが続くため、2番目のメッセージは正常に処理されません。

以下は、読み取りリスナースレッドである、select()(非同期スレッドによって処理されます)を処理するためのサーバーのコードです。

while (!isClosed.get()) {
    if (readSelector.select() == 0) {
        //这里有一个等待操作,等待注册结束
        waitSelection(inRegInput);
        continue;
    }
    Iterator<SelectionKey> iterator = readSelector.selectedKeys().iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isValid()) {
            // 取消继续对keyOps的监听
            key.interestOps(key.readyOps() & ~SelectionKey.OP_READ);
            //线程池执行read操作
            inputHandlePool.execute(new InputHandlerImpl(key));
        }
    }
}

新しいチャネルの準備ができると、select()は0より大きい数値を返します。チャネルの準備ができていない場合、select()はブロックされたままで戻りません。しかし今問題は、なぜすぐに0を返すのですか?

理由

Stack Overflowで答えを見つけました:

Java NIOセレクタselect()はチャネルが準備されているにもかかわらず0を返します

実際、select()が戻るかどうかは、selectedKeysコレクションに関連しています。selectedKeysコレクションが空でない場合、select()はすぐに戻りますが、その戻り値は、変更されたキーの数、つまり新しい準備ができたチャネルの数であり、ここでは1にすることはできません。したがって、このシナリオでは、最初のメッセージが新しいキーを生成し、処理後に削除しなかったため、2番目のメッセージを受信すると、このキーが変更されていないと判断され、select()が0を返します。これにより、無限ループが発生します。したがって、キーはselectedKeysコレクションから削除する必要があります。

解決策:これは非常に簡単です。ループにiterator.remove()を追加して、反復子を走査して処理済みのキーを削除します。コードは次のとおりです。

while (iterator.hasNext()) {
    SelectionKey key = iterator.next();
    iterator.remove();
    ......
}

おすすめ

転載: www.cnblogs.com/buptleida/p/12713514.html