专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
芋道源码  ·  面试官:为什么数据库连接很消耗资源? ·  昨天  
芋道源码  ·  我用这11招,让接口性能提升了100倍 ·  昨天  
芋道源码  ·  项目终于用上了 Spring 状态机,太优雅了! ·  2 天前  
ImportNew  ·  被微软裁员后,3 人自杀! ·  4 天前  
51好读  ›  专栏  ›  ImportNew

Selector 实现原理

ImportNew  · 公众号  · Java  · 2017-08-16 12:00

正文

请到「今天看啥」查看全文


}

}


① 如果配置了“java.nio.channels.spi.SelectorProvider”属性,则通过该属性值load对应的SelectorProvider对象,如果构建失败则抛异常。


② 如果provider类已经安装在了对系统类加载程序可见的jar包中,并且该jar包的源码目录META-INF/services包含有一个java.nio.channels.spi.SelectorProvider提供类配置文件,则取文件中第一个类名进行load以构建对应的SelectorProvider对象,如果构建失败则抛异常。


③ 如果上面两种情况都不存在,则返回系统默认的SelectorProvider,即,sun.nio.ch.DefaultSelectorProvider.create();


④ 随后在调用该方法,即SelectorProvider.provider()。则返回第一次调用的结果。


不同系统对应着不同的sun.nio.ch.DefaultSelectorProvider



这里我们看linux下面的sun.nio.ch.DefaultSelectorProvider


public class DefaultSelectorProvider {

/**

* Prevent instantiation.

*/

private DefaultSelectorProvider() { }

/**

* Returns the default SelectorProvider.

*/

public static SelectorProvider create() {

return new sun.nio.ch.EPollSelectorProvider();

}

}


可以看见,linux系统下sun.nio.ch.DefaultSelectorProvider.create(); 会生成一个sun.nio.ch.EPollSelectorProvider类型的SelectorProvider,这里对应于linux系统的epoll


接下来看下 selector.open():


/**

* Opens a selector.

*

*

The new selector is created by invoking the {@link

* java.nio.channels.spi.SelectorProvider#openSelector openSelector} method

* of the system-wide default {@link

* java.nio.channels.spi.SelectorProvider} object.

*

* @return  A new selector

*

* @throws  IOException

*          If an I/O error occurs

*/

public static Selector open() throws IOException {

return SelectorProvider.provider().openSelector();

}


在得到sun.nio.ch.EPollSelectorProvider后调用openSelector()方法构建Selector,这里会构建一个EPollSelectorImpl对象。


EPollSelectorImpl


class EPollSelectorImpl

extends SelectorImpl

{

// File descriptors used for interrupt

protected int fd0;

protected int fd1;

// The poll object

EPollArrayWrapper pollWrapper;

// Maps from file descriptors to keys

private Map fdToKey;


EPollSelectorImpl(SelectorProvider sp) throws IOException {

super(sp);

long pipeFds = IOUtil.makePipe(false);

fd0 = (int) (pipeFds >>> 32);

fd1 = (int) pipeFds;

try {

pollWrapper = new EPollArrayWrapper();

pollWrapper.initInterrupt(fd0, fd1);

fdToKey = new HashMap<>();

} catch (Throwable t) {

try {

FileDispatcherImpl.closeIntFD(fd0);

} catch (IOException ioe0) {

t.addSuppressed(ioe0);

}

try {

FileDispatcherImpl.closeIntFD(fd1);

} catch (IOException ioe1) {

t.addSuppressed(ioe1);

}

throw t;

}

}


EPollSelectorImpl构造函数完成:


① EPollArrayWrapper的构建,EpollArrayWapper将Linux的epoll相关系统调用封装成了native方法供EpollSelectorImpl使用。


② 通过EPollArrayWrapper向epoll注册中断事件


void initInterrupt(int fd0, int fd1) {

outgoingInterruptFD = fd1;

incomingInterruptFD = fd0;

epollCtl(epfd, EPOLL_CTL_ADD, fd0, EPOLLIN);

}


③ fdToKey:构建文件描述符-SelectionKeyImpl映射表,所有注册到selector的channel对应的SelectionKey和与之对应的文件描述符都会放入到该映射表中。


EPollArrayWrapper


EPollArrayWrapper完成了对epoll文件描述符的构建,以及对linux系统的epoll指令操纵的封装。维护每次selection操作的结果,即epoll_wait结果的epoll_event数组。


EPollArrayWrapper操纵了一个linux系统下epoll_event结构的本地数组。


* typedef union epoll_data {

*     void *ptr;

*     int fd;

*     __uint32_t u32;

*     __uint64_t u64;

*  } epoll_data_t;

*

* struct epoll_event {

*     __uint32_t events;

*     epoll_data_t data;

* };







请到「今天看啥」查看全文