NIO 之 Selector實現原理
相關文章
概述
Selector允許單執行緒處理多個 Channel。如果你的應用打開了多個連線(通道),但每個連線的流量都很低,使用Selector就會很方便。例如,在一個聊天伺服器中。
這是在一個單執行緒中使用一個Selector處理3個Channel的圖示:
要使用Selector,得向Selector註冊Channel,然後呼叫它的select()方法。這個方法會一直阻塞到某個註冊的通道有事件就緒。一旦這個方法返回,執行緒就可以處理這些事件,事件的例子有如新連線進來,資料接收等。
Selector 作用
僅用單個執行緒來處理多個Channels的好處是,只需要更少的執行緒來處理通道。事實上,可以只用一個執行緒處理所有的通道。對於作業系統來說,執行緒之間上下文切換的開銷很大,而且每個執行緒都要佔用系統的一些資源(如記憶體)。因此,使用的執行緒越少越好。
Selector能夠在單個執行緒中處理多個通道,這樣可以減少多個執行緒造成上下文切換問題。
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的幾個方法
register 方法
該方法是在 Channel的register方法中呼叫的。具體詳見NIO 之 Channel實現原理
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 感興趣事件中的一個或多個。
- select()
獲取就緒的 Channel,阻塞方法,沒有就緒的 Channel 就一直阻塞該執行緒。
public int select() throws IOException {
return select(0);
}
- 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);
}
- selectNow()
獲取就緒的 Channel,如果沒有就緒的就直接返回,不阻塞當前執行緒。
public int selectNow() throws IOException {
return lockAndDoSelect(0);
}
上面三個 select方法底層都是呼叫 lockAndDoSelect 方法。
lockAndDoSelect方法的引數值 說明:
-1 : 一直阻塞,直到有就緒的 Channel 可處理
0 : 不阻塞
0: 表示阻塞多長時間
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 中取消註冊該Channelpublic 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()
獲取附件資訊