スケーラブルなサーバーを作成するために、NIO とマルチスレッドの読み取り処理を組み合わせようとしています。Netty や MINA などのフレームワークは使用できません。低レベルのクライアント/サーバー プロトコルの詳細がいくつかあるため、どちらにも実装するには多大な労力がかかります。コードを調べていたところ、次のスニペットに潜在的な競合状態があることに気付きました。
//executes in selector thread
public void runSelector() {
//...
Set<SelectionKey> keys = selector.selectedKeys();
for (Iterator<SelectionKey> keyIter = keys.iterator(); keyIter.hasNext(); ) {
final SelectionKey key = keyIter.next();
keyIter.remove();
if (key.isValid() && key.isReadable()) { //point A
//maybe some other short calculations
((SocketChannel) key.channel()).read(buffer); //point B
workerThreadPool.submit(new Runnable() { public void run() { processRead(key, buffer); } });
}
}
//...
}
//executes in a worker thread
private void processRead(SelectionKey key, ByteBuffer buf) {
//... somewhere
key.cancel();
//...
}
これは非常にありそうもないイベントですが、runSelector() メソッドでコメントした 2 つのポイントの間にセレクター スレッドがあるときに、ワーカー スレッドで key.cancel() を呼び出す可能性は十分にあります。これは同時実行性の高いマシンに展開される可能性があり、セレクター スレッドを実行する CPU コアが停止する可能性があることに注意してください。セレクタ スレッドの key.isReadable() と channel.read() の間のワーカー スレッドで key.cancel() が呼び出され、CancelledKeyException が発生する可能性があるという懸念は妥当ですか? runSelector() が反復の最後にそれらすべてをキャンセルできるように、キャンセルされるすべてのキーを格納するスレッドセーフなコレクションが必要ですか? Netty や MINA などのより専門的なプロジェクトは、このようなケースをどのように処理しますか?