NIO(四、Selector)

6166 ワード

目次


NIO(一、概説)NIO(二、Buffer)NIO(三、Channel)NIO(四、Selector)

Selector


前の2つの章ではBufferとChannelについて説明していますが、この章ではNIOの3つの最も核心的な部分の最後の内容であるセレクタについて説明します.

使用方法


前の章では、1つのスレッドがセレクタによって複数のチャネルを処理および管理する多重化について説明した.このように、セレクタは、複数のチャネルを処理し、そのチャネルイベントを傍受するためのコンポーネントである.
  • Create Open()を呼び出すだけでSelectorオブジェクトを作成できます:
  • Selector selector = Selector.open();
    
  • Register register()メソッドによるチャネル登録:
  • 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オブジェクトも返します.
  • Attach Object登録チャネルのregister()メソッドには、セレクタにチャネルを登録する際に、持参したい追加オブジェクトを選択できるリロードメソッドがあります:
  • 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);
    
  • Blockは、スレッドがセレクタを介してチャネルを操作するため、セレクタは、チャネルを操作するときに、必ず1つのチャネルを処理するときに、別のイベントが準備されたチャネルが待機状態にある.このチャネルを操作するには、チャネルイベントが準備されていることを確認します.レジストリメソッドregisterを使用して使用されるコードの例については、サーバSocketChannelオブジェクトをセレクタに登録するとともに、このチャネルのOP_に注目します.ACCEPT操作タイプイベントでは、このチャネルを操作できるacceptイベントがいつ準備されているかを判断できます.セレクタは、セレクタ内のチャネルが注目するイベントが準備されるまでブロックするかどうかを選択し、プログラムを下に実行し続ける3つのリロードselect()メソッドを提供します.まずselect()メソッドを見てください.このメソッドは、セレクタ内のチャネルが注目するイベントが準備できるまでブロックされます.
  • selector.select();
    

    パラメータ5000は5秒で、パラメータはミリ秒単位です.この方法は5秒間ブロックされ、5秒以内にチャネルイベントが準備されていない場合、プログラムは次のように実行されます.
    selector.select(5000);
    

    selectNow()は非ブロックであり、チャネルイベントが準備されているかどうかにかかわらず、プログラムは下に実行されます.
    selector.selectNow();
    

    この3つの方法の本質的な違いは、セレクタが1つまたは複数のチャネルのイベント準備をブロックしたり、待機したりするのにどれくらいの時間がかかるかにほかならない.
  • Keys&SelectionKeysチャネルのregister()登録メソッドを実行するたびに、SelectionKeysが返されます.このセレクタのすべての準備済みSelectionKeyyはselectedKeys()によって取得されます.
  • 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();
    
  • wake up selectorオブジェクトのselect()またはselect(long)メソッドを使用する場合、現在のスレッドがずっとブロックされている可能性が高いので、別のスレッドでselectorを実行する.wakeUp()メソッドは、現在ブロックされているスレッドを呼び出し、select()をすぐに返します.もちろん、現在のスレッドがブロックされていない場合、wakeUp()メソッドが実行されると、次のスレッドのselect()メソッドがすぐに返され、ブロックされなくなります.
  • closeは、close()メソッドが現在のセレクタを閉じることができることを明らかにした.スレッドが現在ブロックされている場合、この状態を中止するにはセレクタのwakeUp()メソッドを実行する必要があります.close()メソッドの実装は、ブロックされたスレッドを呼び出してから、次の操作を続行します.次に、すべてのチャネル、すべての準備ができているSelectionKeyが空になり、このセレクタのポーリングコンポーネントもアイドルになります.

  • 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);
    }
    
  • interestOps&readyOps SelectionKeyの実装クラスで定義された変数:
  • 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;
    }
    
  • Channel、SelectorがSelectionKeyでチャネルまたはセレクタを取得するには、そのうちの2つのメソッドを呼び出すだけでよい:
  • .
    SelectableChannel selectableChannel = selectionKey.channel();
    Selector sel = selectionKey.selector();