先看官方描述
/**
* A channel that can be multiplexed via a {@link Selector}.
* <p> In order to be used with a selector, an instance of this class must
* first be <i>registered</i> via the {@link #register(Selector,int,Object)
* register} method. This method returns a new {@link SelectionKey} object
* that represents the channel's registration with the selector.
*其他略。。。
*/
简单总结:
它通道的注册使用大致过程如下:
图片.png SocketChannel的类图
图片.png
Selector selector = Selector.open(); 跟踪selector的源码如下:
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
继续往下跟踪就是我们要介绍的关键了SelectorProvider.provider()
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
//AccessController.doPrivileged属于特权操作,下面详说
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
//loadProviderFromProperty方法是通过JDK的参数//-Djava.nio.channels.spi.SelectorProvider=class设置的class来反射构造SelectorProvider
if (loadProviderFromProperty())
return provider;
//loadProviderAsService从jar中的目录META-INF/services配置文件中找参数//java.nio.channels.spi.SelectorProvider=class设置的第一个class来反射构造SelectorProvider
if (loadProviderAsService())
return provider;
//最后都没有则调用不同操作系统版本的JDK里自带的sun.nio.ch.DefaultSelectorProvider来创建SelectorProvider
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
先解释下AccessController访问控制类的三个功能
1、根据当前有效的安全策略,决定是允许还是拒绝对关键系统资源的访问,
2、将代码标记为“特权”,从而影响后续访问确定
3、获取当前调用上下文的“快照”,因此可以针对保存的上下文进行来自不同上下文的访问控制决策。
上面的特权操作主要使用了功能2,部分javadoc中伪代码如下:
//类注释
/**
* somemethod() {
* ...normal code here...
* String user = AccessController.doPrivileged(
* new PrivilegedAction<String>() {
* public String run() {
* return System.getProperty("user.name");
* }
* });
* ...normal code here...
* }}
* 其他略。。
**/
public final class AccessController {
//方法注释
/**
* Performs the specified {@code PrivilegedAction} with privileges
* enabled. The action is performed with <i>all</i> of the permissions
* possessed by the caller's protection domain.
* 其他略。。
*/
//简言之就是让调用者有权执行方法代码中的action
@CallerSensitive
public static native <T> T doPrivileged(PrivilegedAction<T> action);
//其他略。。
}
总结SelectorProvider的创建分三步进行: (1)由JDK的参数-Djava.nio.channels.spi.SelectorProvider=class设置的class来反射构造SelectorProvider,找不到就跳转到步骤(2) (2)从jar中的目录META-INF/services配置文件中找参数java.nio.channels.spi.SelectorProvider=class设置的第一个class来反射构造SelectorProvider,找不到就跳转到步骤(3) (3)调用不同操作系统版本的JDK里自带的sun.nio.ch.DefaultSelectorProvider来创建SelectorProvider 一般都会走到最后的步骤(3),而这个步骤里创建的SelectorProvider在各个操作系统对应的JDK里各不相同。sun.nio.ch.DefaultSelectorProvider这个类最终编译后放置在JDK的安装根目录下的jre/lib/rt.jar里。
不通系统的DefaultSelectorProvider实现
public class DefaultSelectorProvider {
//
public static SelectorProvider create() {
return new WindowsSelectorProvider();
}
}
public class DefaultSelectorProvider {
public static SelectorProvider create()
{
return new KQueueSelectorProvider();
}
}
public class DefaultSelectorProvider {
public static SelectorProvider create()
{
String str1 = (String)AccessController.doPrivileged(new GetPropertyAction("os.name"));
if ("SunOS".equals(str1)) {
return new DevPollSelectorProvider();
}
if ("Linux".equals(str1)) {
String str2 = (String)AccessController.doPrivileged(new GetPropertyAction("os.version"));
String[] arrayOfString = str2.split("\\.", 0);
if (arrayOfString.length >= 2) {
try {
int i = Integer.parseInt(arrayOfString[0]);
int j = Integer.parseInt(arrayOfString[1]);
if ((i > 2) || ((i == 2) && (j >= 6))) {
return new EPollSelectorProvider();
}
}
catch (NumberFormatException localNumberFormatException)
{
}
}
}
return new PollSelectorProvider();
}
}
那么为什么会出现几种不同的实现方式呢?原因在于 不同操作系统的I/O多路复用选择器各自内核实现不同,目前有select、poll、epoll、kqueue四种实现
操作系统下JDK | JDK里SelectorProvider实现 |
---|---|
windows JDK | sun.nio.cn.WindowsSelectorProvider |
MAC JDK | sun.nio.ch.KQueueSelectorProvider |
Linux JDK | 1)os.name=SunOS时,sun.nio.ch.DevPollSelectorProvider2)os.name=Linux并且os.version版本号中第一个版本大于2或者第一个版本号等于2且第二个版本大于6时,sun.nio.chEPollSelectorProviderLinux kernels 2.6内核版本及以后使用epoll进行支持;Linux kernels 2.6内核版本之前使用poll进行支持;比如Linux version 3.10.0-327.el7.x86_64就是用的 |