NIO 之 Selector實現原理

相關文章

NIO 之 ByteBuffer實現原理
NIO 之 Channel實現原理
BIO、NIO、AIO 內部原理分析服務器

概述

Selector容許單線程處理多個 Channel。若是你的應用打開了多個鏈接(通道),但每一個鏈接的流量都很低,使用Selector就會很方便。例如,在一個聊天服務器中。ide

這是在一個單線程中使用一個Selector處理3個Channel的圖示:源碼分析

selector與channel關係spa

要使用Selector,得向Selector註冊Channel,而後調用它的select()方法。這個方法會一直阻塞到某個註冊的通道有事件就緒。一旦這個方法返回,線程就能夠處理這些事件,事件的例子有如新鏈接進來,數據接收等。操作系統

Selector 做用

僅用單個線程來處理多個Channels的好處是,只須要更少的線程來處理通道。事實上,能夠只用一個線程處理全部的通道。對於操做系統來講,線程之間上下文切換的開銷很大,並且每一個線程都要佔用系統的一些資源(如內存)。所以,使用的線程越少越好。
Selector可以在單個線程中處理多個通道,這樣能夠減小多個線程形成上下文切換問題。.net

Selector 源碼分析

public abstract class Selector implements Closeable {
    protected Selector() { }
    public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }
    public abstract boolean isOpen();
    public abstract SelectorProvider provider();
    public abstract Set<SelectionKey> keys();
    public abstract Set<SelectionKey> selectedKeys();
    public abstract int selectNow() throws IOException;
    public abstract int select(long timeout) throws IOException;
    public abstract int select() throws IOException;
    public abstract Selector wakeup();
    public abstract void close() throws IOException;

Selector 是個抽象類,提供一個靜態的方法獲取Selector子類SelectorImpl的實例。線程

下面分析Selector的幾個方法3d

register 方法

該方法是在 Channel的register方法中調用的。具體詳見NIO 之 Channel實現原理rest

register 方法code

  1. 經過channel和selector構造一個SelectionKey的實例。
  2. SelectionKey 註冊感興趣的事件

這四種事件用SelectionKey的四個常量來表示:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE

select 方法

不一樣的 Channel 註冊到 Selector 後,就能夠隨時查詢 Selector ,找出哪些 Channel 已經準備好能夠進行處理。Channel 可能準備好上面註冊到 Selector 感興趣事件中的一個或多個。

  1. select()
    獲取就緒的 Channel,阻塞方法,沒有就緒的 Channel 就一直阻塞該線程。
public int select() throws IOException {
    return select(0);
}
  1. select(long timeout)
    獲取就緒的 Channel, 阻塞方法,阻塞 timeout 時間,若是超時尚未就緒的 Channel,返回0,不作任何操做。
public int select(long timeout)
        throws IOException
    {
        if (timeout < 0)
            throw new IllegalArgumentException("Negative timeout");
        return lockAndDoSelect((timeout == 0) ? -1 : timeout);
    }
  1. selectNow()
    獲取就緒的 Channel,若是沒有就緒的就直接返回,不阻塞當前線程。
public int selectNow() throws IOException {
        return lockAndDoSelect(0);
    }

上面三個 select方法底層都是調用 lockAndDoSelect 方法。
lockAndDoSelect方法的參數值 說明:
-1 : 一直阻塞,直到有就緒的 Channel 可處理
0 : 不阻塞
timout>0: 表示阻塞多長時間(timeout)

keys 方法

獲取全部註冊到 Selector 上的 SelectionKey
 public Set<SelectionKey> keys() {
    if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException();
        return publicKeys;
}

selectedKeys 方法

獲取全部註冊到 Selector 上就緒 Channel 的 SelectionKey 信息。

public Set<SelectionKey> selectedKeys() {
    if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException();
        return publicSelectedKeys;
}

SelectionKey 解析

SelectionKey 類結構以下:

public abstract class SelectionKey {
    protected SelectionKey() { }
    public static final int OP_READ = 1 << 0;
    public static final int OP_WRITE = 1 << 2;
    public static final int OP_CONNECT = 1 << 3;
    public static final int OP_ACCEPT = 1 << 4;
    //附件信息
    private volatile Object attachment = null;
    ....
}
  • public abstract SelectableChannel channel()
    獲取channel對象

  • public abstract Selector selector()
    獲取seletor對象

  • public abstract void cancel()
    從 Selector 中取消註冊該Channel

  • public abstract int interestOps()
    獲取該chennel 註冊到 selector 上的事件

  • public abstract SelectionKey interestOps(int ops)
    修改註冊到 selector 上的事件

  • public abstract int readyOps()
    是否讀就緒

讀就緒不等於可讀,若是沒有註冊讀事件是不能讀的。

  • public final boolean isReadable()
    判斷是否可讀

  • public final boolean isWritable()
    是否可寫

  • public final boolean isConnectable()
    是否已經鏈接

  • public final Object attach(Object ob)
    添加附件信息

  • public final Object attachment() 獲取附件信息

相關文章
相關標籤/搜索