Java NIO 選擇器(Selector)的內部實現(poll epoll)
Java NIO中的選擇器依賴作業系統核心的這些系統呼叫,我們這裡只講解與linux核心相關的NIO實現,當然,windows或其他作業系統實現大體上是類似的,相信大家也可以觸類旁通。
那麼,本文從這裡將從簡到難,一步一步為大家講解選擇器的點點滴滴吧。
選擇器的巨集觀理解
“有這麼一種檢查員,她工作在養雞場,每天的工作就是不停的檢視特定的雞舍,如果有雞生蛋了,或者需要餵食,或者有雞生病了,就把相應資訊記錄下來,這樣一來,雞舍負責人想知道雞舍的情況,只需要到檢查員那裡查詢即可,當然,雞舍負責人得事先告知檢查員去查詢哪些雞舍。“
以上這段話即為選擇器所做工作的一個比喻,實際上選擇器為通道服務,通道事先告訴選擇器:“我對某些事件感興趣,如可讀、可寫等“,選擇器在接受了一個或多個通道的委託後,開始選擇工作,它的選擇工作就完全交給作業系統,linux下即為poll或epoll。
選擇器的建立
當呼叫Selector.open()時,選擇器通過專門的工廠SelectorProvider來建立Selector的實現,SelectorProvider遮蔽了不同作業系統及版本建立實現的差異性。具體實現程式碼如下:
java.nio.channels.Selector
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
因為SelectorProvider本身為一個抽象類,通過呼叫provider()提供對應的Provider實現,如PollSelectorProvider、EPollSelectorProvider
java.nio.channels.spi.SelectorProvider
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return (SelectorProvider)AccessController
.doPrivileged(new PrivilegedAction() {
public Object run() {
if (loadProviderFromProperty())
return provider;
if (loadProviderAsService())
return provider;
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
預設的Provider實現即為DefaultSelectorProvider,通過呼叫create(),得到具體的SelectorProvider
sun.nio.ch.DefaultSelectorProvider
public static SelectorProvider create() {
PrivilegedAction pa = new GetPropertyAction("os.name");
String osname = (String) AccessController.doPrivileged(pa);
if ("SunOS".equals(osname)) {
return new sun.nio.ch.DevPollSelectorProvider();
}
// use EPollSelectorProvider for Linux kernels >= 2.6
if ("Linux".equals(osname)) {
pa = new GetPropertyAction("os.version");
String osversion = (String) AccessController.doPrivileged(pa);
String[] vers = osversion.split("\\.", 0);
if (vers.length >= 2) {
try {
int major = Integer.parseInt(vers[0]);
int minor = Integer.parseInt(vers[1]);
if (major > 2 || (major == 2 && minor >= 6)) {
return new sun.nio.ch.EPollSelectorProvider();
}
} catch (NumberFormatException x) {
// format not recognized
}
}
}
return new sun.nio.ch.PollSelectorProvider();
}
這是linux作業系統下的DefaultSelectorProvider的實現,可以看到,如果核心版本>=2.6則,具體的SelectorProvider為EPollSelectorProvider,否則為預設的PollSelectorProvider
結合上文,可以猜測一下EPollSelectorProvider提供的Selector肯定是與核心epoll有關的,PollSelectorProvider提供的
Selector肯定是與poll有關的。的確如此:
sun.nio.ch.EPollSelectorProvider
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
sun.nio.ch.PollSelectorProvider
public AbstractSelector openSelector() throws IOException {
return new PollSelectorImpl(this);
}