一.channel.spi底层实现我们首先看看SelectorProvider:
SelectorProvider提供了通过底层输出各种NIO实现的接口:
- public abstract DatagramChannel openDatagramChannel():创建并打开一个DatagramChannel...DatagramChannel.open()方法调用.
- public abstract SocketChannel openSocketChannel():创建并打开一个SocketChannel,SocketChannel.open()方法调用
- public abstract ServerSocketChannel openServerSocketChannel():由ServerSocketChannel.open()方法调用
- public abstract SocketChannel openSocketChannel():由SocketChannel.open()方法调用.
- public abstract Pipe openPipe():由Pile.open()调用
- public abstract AbstractSelector openSelector():此方法由Selector.open()调用
- public static SelectorProvider provider():获得当前应用所使用的provider;不同的OS,对IO Selector模型各不相同,JAVA API中也提供了与其适配的provider,那么对provider的选择是怎么做的呢?在静态方法provider中执行如下:
- 如果系统定义了"java.nio.channels.spi.SelectorProvider"参数,例如-Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider,那么系统将会加载并使用此provider.(loadProviderFromProperty()).
- 如果对系统类加载器可见的jar文件中安装了provider,并且jar文件包含资源目录META-INF/services中名为java.nio.channels.spi.SelectorProvider的提供者配置文件,则遍历provider列表,并加载和初始化第一个类名称.
- 如果上述,都不能获得provider,将会通过DefaultSelectorProvider.create()获得结果.前两步均可以被应用开发者所操作.对于3)返回的结果可能在不同的JDK版本下有所不同.
/代码样例(来自openJDK)
public static SelectorProvider create() {
PrivilegedAction pa = new GetPropertyAction("os.name");
String osname = (String) AccessController.doPrivileged(pa);
if ("SunOS".equals(osname)) {//1、如果SunOS
return new sun.nio.ch.DevPollSelectorProvider();
}
//2、Linux 内核>=2.6
// 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();
}
可以简单归纳如下:在Solaris系统下将会使用DevPollSelectorProdiver;在Linux系统下(2.6+版本),将会使用EPollSelectorProvider;否则将会使用PollSelectorProvider.对于SunJDK,DefaultSelectorProvider将直接返回WindowsSelectorProvider.这是一种基于Poll机制.
二.多路IO复用模型:
目前广泛支持的多路IO复用模型:epoll,poll,devpoll,select.
- select:同步IO,目前已经不能再被推荐使用.缺点:单个进程能够监视(打开)的FD(文件描述符)的个数是有最大限制的(2048);select需要复制大量的句柄数据结构,产生巨大的开销;应用程序需要遍历句柄列表才能知道哪个句柄(socket)发生了事件.
- epoll:一种被认为性能最好的IO多路复用模型.这是一种异步IO,它能在大量并发连接环境中,显著提高性能.因为它互惠复用文件描述符集合来传递结果而迫使开发者每次都必须重新准备要被侦听的文件描述符集合,另一点原因是,获取事件时无需遍历整个被侦听的描述符集,而只需要遍历那些被内核IO事件异步唤醒加入Ready队列的描述符集合就行了.(事件可以被水平触发/边缘触发)
- 因为IO模型存在历史原因,它所涉及到的内容相当复杂,我们到此为止吧.