NIO(四、Selector)
6166 ワード
目次
NIO(一、概説)NIO(二、Buffer)NIO(三、Channel)NIO(四、Selector)
Selector
前の2つの章ではBufferとChannelについて説明していますが、この章ではNIOの3つの最も核心的な部分の最後の内容であるセレクタについて説明します.
使用方法
前の章では、1つのスレッドがセレクタによって複数のチャネルを処理および管理する多重化について説明した.このように、セレクタは、複数のチャネルを処理し、そのチャネルイベントを傍受するためのコンポーネントである.
Selector selector = Selector.open();
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_ACCEPT);
チャネルを登録する前に、チャネルを非ブロックモードに設定し、ソースコードを確認するとregister()は現在のチャネルが非ブロックモードであるかどうかを検証し、ブロックモードである場合、IllegalBlockingModeException異常を放出します.前の章でも述べたように、なぜFileChannelはSelectableChannelを継承していないのか、多重化を必要としないため、チャネルを使用する場合、FileChannelだけがセレクタにチャネルを登録することができず、SelectableChannelを継承する場合はセレクタにチャネルを注記することができる.登録チャネルメソッドの2番目のパラメータは、SelectionKeyで定義された操作タイプです.このチャネルがサポートされている限り、register()メソッドを実行するときにも、チャネルが操作をサポートできるかどうかを確認するために、興味のある操作タイプを入力できます.登録メソッドは、selectionKeyオブジェクトも返します.
public abstract SelectionKey register(Selector sel, int ops, Object att)
throws ClosedChannelException;
たとえば、使用時に前の文字列を添付します.
String ch_name = "123";
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_ACCEPT,ch_name);
この文字列を取得するには、attachment()を使用します.
//
String ch_name_accept = (String) selectionKey.attachment();
もちろん、登録時に返されるSelectionKeyオブジェクトは、使用時に必要な追加オブジェクトを追加することもできます.
selectionKey.attach(ch_name);
selector.select();
パラメータ5000は5秒で、パラメータはミリ秒単位です.この方法は5秒間ブロックされ、5秒以内にチャネルイベントが準備されていない場合、プログラムは次のように実行されます.
selector.select(5000);
selectNow()は非ブロックであり、チャネルイベントが準備されているかどうかにかかわらず、プログラムは下に実行されます.
selector.selectNow();
この3つの方法の本質的な違いは、セレクタが1つまたは複数のチャネルのイベント準備をブロックしたり、待機したりするのにどれくらいの時間がかかるかにほかならない.
Set selectionKeys = selector.selectedKeys();
一般的に、この方法はselect()の後に実行されます.これは、このポーリングによって各準備完了チャネルがポーリングされることを意味するためです.
Iterator iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
//
}
//
iterator.remove();
}
ここでは、準備されたチャネルについて説明します.これにより、keys()メソッドですべてのSelectionKeyセットを取得できます.
Set keys = selector.keys();
SelectionKey
SelectionKeyの機能は、チャネルの登録トークンに似ています.
このクラスは4つの操作タイプを定義し、各操作タイプは対応するイベントに対応し、これらのいくつかの異なるイベントをリスニングすることで、イベントをトリガーするときに対応する操作が準備されていることを示します.
|操作タイプ|値|説明|----|----|----|----|OP_READ|1<<0|読み出し操作||OP_WRITE|1<<2|書き込み操作||OP_CONNECT|1<<3|接続ソケット操作||OP_ACCEPT|1<<4|socket操作を受け入れる|ここでは、SelectableChannelを継承するすべてのチャネルがサポートできる操作タイプを定義し、SocketChannelがread、write、connectをサポートするなど、特定のチャネルのvalidOps()メソッドで表示できます.
// SocketChannel
public final int validOps() {
return (SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT);
}
private volatile int interestOps;
private int readyOps;
interestOpsは、関心のあるオペレーションセットを格納するために使用され、readyOpsは、準備されたオペレーションセットを格納するために使用される.ここでinterestOps()メソッドとnioInterestOps()はいずれもinterestOpsを返し、interestOps()はcancel()が実行されたかどうかを検証し、キャンセルされた場合はCancelledKeyException例外を放出します.readyOpsにもreadyOps()とnioReadyOps()の方法があり、論理はinterestOpsとほぼ一致している.このSelectionKey抽象クラスが実装されたコードを観察します.
// SelectionKey
public final boolean isAcceptable() {
return (readyOps() & OP_ACCEPT) != 0;
}
アクセスが完了したかどうかを判断すると、readyOps()が対応する操作タイプと一致する限り、ゼロでない場合はtrueが返され、リクエストを受け入れる操作が完了したことを示します.これはselectionKeyが提供した方法ですが、selectionKeyは同じようにbooleanに戻ってinterestOpsセットに操作が存在するかどうかを判断する方法を提供していません.私たちは自分でこれらの方法を実現することができます.
private boolean isInterestRead(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_READ;
}
private boolean isInterestWrite(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;
}
private boolean isInterestConnect(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
}
private boolean isInterestAccept(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
}
SelectableChannel selectableChannel = selectionKey.channel();
Selector sel = selectionKey.selector();