問題
今日、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();
......
}