作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题
代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等
上篇文章详细介绍了 Selector 的核心原理和使用方法,这篇文章我们来深入了解 Selector 的源码,主要讲三个最常用的方法 open()
,register()
和 selector()
。
open()
调用 Selector.open()
方法会创建一个 Selector 实例。
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
SelectorProvider 是一个抽象类,它提供了创建 Selector 、ServerSocketChannel、SocketChannel 的方法,采用 Java 的 SPI 方式实现。
public static SelectorProvider provider() {
synchronized (lock) {
// 这里保证了只有一个 SelectorProvider
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
// 通过配置的 java.nio.channels.spi.SelectorProvider 值注入自定义的SelectorProvider
if (loadProviderFromProperty())
return provider;
// 通过 ServiceLoad 注入,然后获取配置的第一个服务
if (loadProviderAsService())
return provider;
// 默认
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
如果没有特殊配置,我们一般都是使用默认方法 sun.nio.ch.DefaultSelectorProvider.create()
。平台的不同 DefaultSelectorProvider 的实现就不同,码哥这里是 MacBook,实现如下:
public class DefaultSelectorProvider {
private DefaultSelectorProvider() {
}
public static SelectorProvider create() {
return new KQueueSelectorProvider();
}
}
从这里我们就可以看出,MacOS 使用的多路复用是 kqueue。对于 Windows 和 Linux 码哥也找到了相对应的实现。
// Windows
public class DefaultSelectorProvider {
private DefaultSelectorProvider() { }
public static SelectorProvider create() {
return new sun.nio.ch.WindowsSelectorProvider();
}
}
// Linux
public class DefaultSelectorProvider {
private DefaultSelectorProvider() { }
@SuppressWarnings("unchecked")
private static SelectorProvider createProvider(String cn) {
Class<SelectorProvider> c;
try {
c = (Class<SelectorProvider>)Class.forName(cn);
} catch (ClassNotFoundException x) {
throw new AssertionError(x);
}
try {
return c.newInstance();
} catch (IllegalAccessException | InstantiationException x) {
throw new AssertionError(x);
}
}
public static SelectorProvider create() {
String osname = AccessController
.doPrivileged(new GetPropertyAction("os.name"));
if (osname.equals("SunOS"))