Selector open流程
Selector.open()
可以看到Selector是由Selector.provider()提供的provider提供的.所以接下来看看SelectorProvider::provider方法
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
SelectorProvider.provider()
windows这边基本上都是走WindowsSelectorProvider了.另一种linux的还没看.
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
//从系统环境变量中"java.nio.channels.spi.SelectorProvider"加载Provider实现.
if (loadProviderFromProperty())
return provider;
//通过JAVA SPI加载Provider的实现
if (loadProviderAsService())
return provider;
//默认Provider,我看win版jdk里返回的是WindowsSelectorProvider
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
WindowsSelectorProvider.openSelector()
返回WindowsSelectorImpl.java
public AbstractSelector openSelector() throws IOException {
return new WindowsSelectorImpl(this);
}
register流程
AbstractSelector::register
如上图所示,首先只有SelectableChannel可以注册至selector上,而我们常用到的大部分channel基本都继承自SelectableChannel.
接下来看看AbstractSelectableChannel::register的API介绍
使用给定的Selector注册此Channel,并返回SelectionKey。
如果此Channel已在给定的Selector中注册,则将其兴趣(ops参数)设置为给定值后,返回表示注册的SelectionKey.
若此Channel尚未在给定的Selector中注册,则在保持适当的锁的同时调用AbstractSelector::register方法进行注册.返回key之前先添加进channel的key集合中.(每个channel也维护了自己注册过的selector集合)
public final SelectionKey register(Selector sel, int ops,
Object att) throws ClosedChannelException
{
synchronized (regLock) {
//验证通道是否打开
if (!isOpen())
throw new ClosedChannelException();
//validOps返回子类支持的ops,即对应的位数值为1. 此处校验ops是否有支持权限外的位数值为1
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
//selector不支持阻塞channel注册.此处检查了channel是否为阻塞模式.
if (blocking)
throw new IllegalBlockingModeException();
//检查此channel是否已经注册过Selector了.
SelectionKey k = findKey(sel);
//如果注册过,就更新ops和attachment
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
//如果没有注册过
if (k == null) {
// New registration
synchronized (keyLock) {
//检查连接是否是打开的
if (!isOpen())
throw new ClosedChannelException();
//走selector的register方法注册此channel.
k = ((AbstractSelector)sel).register(this, ops, att);
//并添加至此channel记录的注册的Selector集合.
addKey(k);
}
}
return k;
}
}
看下来,发现channel最后还是通过调用Selector::register方法进行注册,所以接下来看Selector的register方法.
SelectorImpl::register
SelectorImpl实现了Selector的register接口.SelectionKey的创建是在这一步进行的.
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
//只允许SelChImpl实现类可以注册.
if (!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
//创建SelectionKey并返回.
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
var4.attach(var3);
synchronized(this.publicKeys) {
//注册到Selector实现里.
this.implRegister(var4);
}
var4.interestOps(var2);
return var4;
}
}
WindowsSelectorImpl::implRegister
windows下的JDK8默认实现是这个. 总的来说selector维护了一批SelectionKey.
//从SelectorImpl下继承来的.
protected HashSet<SelectionKey> keys = new HashSet();
//一个存放 selectionKey的Map(继承于HashMap)
private final WindowsSelectorImpl.FdMap fdMap = new
WindowsSelectorImpl.FdMap();
//一个用于存放SelectionKey的数组. 此处自己用数组实现增删可能是为了快?
private SelectionKeyImpl[] channelArray = new
SelectionKeyImpl[8];
private int totalChannels = 1;
protected void implRegister(SelectionKeyImpl var1) {
synchronized(this.closeLock) {
if (this.pollWrapper == null) {
throw new ClosedSelectorException();
} else {
//判断数组是否需要扩容
this.growIfNeeded();
//追加至channelArray数组的末尾.
this.channelArray[this.totalChannels] = var1;
//并且selectionKey自己也记录着自己在selector中的哪个位置.
var1.setIndex(this.totalChannels);
//往map集合里存一个
this.fdMap.put(var1);
//往set集合里存一个
this.keys.add(var1);
//存放文件句柄信息( fd ( file description ))
this.pollWrapper.addEntry(this.totalChannels, var1);
++this.totalChannels;
}
}
}
select流程
SelectorImpl.doSelect(long timeout)
WindowsSelectorImpl::doSelect
windows JDK8 默认自带的Selector的doSelect()方法.
//存放着文件句柄,基本用unsafe直接操作内存.
private PollArrayWrapper pollWrapper = new PollArrayWrapper(8);
protected int doSelect(long var1) throws IOException {
if (this.channelArray == null) {
throw new ClosedSelectorException();
} else {
this.timeout = var1;
//清除注销队列(有个集合记录了cancel的key.通过这个方法将其正式清除)
this.processDeregisterQueue();
if (this.interruptTriggered) {
this.resetWakeupSocket();
return 0;
} else {
//启动/销毁WindowsSelectorImpl.SelectThread线程,保证有threadsCount(每增加1024个连接,这个值+1)个线程在工作.每个SelectThread都有一个SubSelector去poll这个Selector里的PollArrayWrapper里的句柄. *推测* 并发分段轮询机制.
this.adjustThreadsCount();
this.finishLock.reset();
this.startLock.startThreads();
try {
this.begin();
try {
//负责一开始1024个channel的poll工作.后面的每1024个连接都将给SelectThread的SubSelector负责poll. 据说系统调用的poll会因为句柄太多影响性能,所以进行拆分.(待证)
this.subSelector.poll();
} catch (IOException var7) {
this.finishLock.setException(var7);
}
if (this.threads.size() > 0) {
this.finishLock.waitForHelperThreads();
}
} finally {
this.end();
}
this.finishLock.checkForException();
//清除Cancel了的连接.
this.processDeregisterQueue();
//更新keys事件. 这个方法的核心在于SubSelector的processSelectedKeys()方法
int var3 = this.updateSelectedKeys();
this.resetWakeupSocket();
return var3;
}
}
}