来看看selector最核心的select()方法吧
我们之前分析了Selector默认是WindowSelectorImpl的一个实例,调用selector(timeout),timout是给Selector设置的时间参数,之后调用lockAndDoSelector,lockAndDoSelector方法核心就是调用抽象方法doSelect,此时加了synchronize同步。我们看下子类的doSelect的实现,默认实现是在WindowsSelectorImpl类下。
protected int doSelect(long var1) throws IOException { if(this.channelArray == null) { throw new ClosedSelectorException(); } else { this.timeout = var1; this.processDeregisterQueue(); if(this.interruptTriggered) { this.resetWakeupSocket(); return 0; } else { this.adjustThreadsCount(); this.finishLock.reset(); this.startLock.startThreads(); try { this.begin(); try { this.subSelector.poll(); } catch (IOException var7) { this.finishLock.setException(var7); } if(this.threads.size() > 0) { this.finishLock.waitForHelperThreads(); } } finally { this.end(); } this.finishLock.checkForException(); this.processDeregisterQueue(); int var3 = this.updateSelectedKeys(); this.resetWakeupSocket(); return var3; } } }
首先调用processDeregisterQueue()方法,把已经解除注册的channel进行注销注册。
void processDeregisterQueue() throws IOException { Set var1 = this.cancelledKeys(); synchronized(var1) { if(!var1.isEmpty()) { Iterator var3 = var1.iterator(); while(var3.hasNext()) { SelectionKeyImpl var4 = (SelectionKeyImpl)var3.next(); try { this.implDereg(var4); } catch (SocketException var11) { throw new IOException("Error deregistering key", var11); } finally { var3.remove(); } } } } }
遍历加到cancelledKeys中的SelectorKey(需要解除注册的key),依次调用implDereg()进行注销。
protected void implDereg(SelectionKeyImpl var1) throws IOException { int var2 = var1.getIndex(); assert var2 >= 0; Object var3 = this.closeLock; synchronized(this.closeLock) { if(var2 != this.totalChannels - 1) { SelectionKeyImpl var4 = this.channelArray[this.totalChannels - 1]; this.channelArray[var2] = var4; var4.setIndex(var2); this.pollWrapper.replaceEntry(this.pollWrapper, this.totalChannels - 1, this.pollWrapper, var2); } var1.setIndex(-1); } this.channelArray[this.totalChannels - 1] = null; --this.totalChannels; if(this.totalChannels != 1 && this.totalChannels % 1024 == 1) { --this.totalChannels; --this.threadsCount; } this.fdMap.remove(var1); this.keys.remove(var1); this.selectedKeys.remove(var1); this.deregister(var1); SelectableChannel var7 = var1.channel(); if(!var7.isOpen() && !var7.isRegistered()) { ((SelChImpl)var7).kill(); } }
如果当前解除注册key的channel是Selector中最后一个,那么直接移除,否则,需要将当前需移除的channel跟最后一个channel调换位置,再移除,以保证数组的连续性,剩下无非是注册的逆操作。
完成真正注销后,然后调用adjustThreadCount()
private void adjustThreadsCount() { int var1; if(this.threadsCount > this.threads.size()) { for(var1 = this.threads.size(); var1 < this.threadsCount; ++var1) { WindowsSelectorImpl.SelectThread var2 = new WindowsSelectorImpl.SelectThread(var1); this.threads.add(var2); var2.setDaemon(true); var2.start(); } } else if(this.threadsCount < this.threads.size()) { for(var1 = this.threads.size() - 1; var1 >= this.threadsCount; --var1) { ((WindowsSelectorImpl.SelectThread)this.threads.remove(var1)).makeZombie(); } } }
该方法是对于线程数量进行调整,要求的线程数量是根据channel数目统计的,每1024个channel注册,则多一个线程来负责。根据现有线程进行动态调整。
之后调用begin()方法
protected final void begin() { if (interruptor == null) { interruptor = new Interruptible() { public void interrupt(Thread ignore) { AbstractSelector.this.wakeup(); }}; } AbstractInterruptibleChannel.blockedOn(interruptor); Thread me = Thread.currentThread(); if (me.isInterrupted()) interruptor.interrupt(me); }
这里先判断interruptor是否为空,空的话则创建一个,这个interruptor保证了:当前线程阻塞在io上时,当其被interruptor时,可以从select阻塞中被唤醒。在begin()完成后,将会调用其subSelector的poll,正式开始select过程。
private int poll() throws IOException { return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout); } private int poll(int var1) throws IOException { return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress + (long)(this.pollArrayIndex * PollArrayWrapper.SIZE_POLLFD), Math.min(1024, WindowsSelectorImpl.this.totalChannels - (var1 + 1) * 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout); } private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);
这里的poll0()是native方法。主要是为了监听pollWrapper中所保存的fd是否有数据进出,如果没有进出,则会在在此处在timeout的时间里一直保持阻塞状态。
当poll取得数据后,会重新检查一遍已经被取消注册的channel,并注销它,之后调用updateSelectedKeys()
private int updateSelectedKeys() { ++this.updateCount; byte var1 = 0; int var4 = var1 + this.subSelector.processSelectedKeys(this.updateCount); WindowsSelectorImpl.SelectThread var3; for(Iterator var2 = this.threads.iterator(); var2.hasNext(); var4 += var3.subSelector.processSelectedKeys(this.updateCount)) { var3 = (WindowsSelectorImpl.SelectThread)var2.next(); } return var4; }这里对所有的线程调用processSelectedKeys来处理所有线程在poll过程中取得的结果并处理。然后返回所有线程中处理的channel数量的总和。我们来看下processSelectedKeys()方法
private int processSelectedKeys(long var1) { byte var3 = 0; int var4 = var3 + this.processFDSet(var1, this.readFds, Net.POLLIN, false); var4 += this.processFDSet(var1, this.writeFds, Net.POLLCONN | Net.POLLOUT, false); var4 += this.processFDSet(var1, this.exceptFds, Net.POLLIN | Net.POLLCONN | Net.POLLOUT, true); return var4; } private int processFDSet(long var1, int[] var3, int var4, boolean var5) { int var6 = 0; for(int var7 = 1; var7 <= var3[0]; ++var7) { int var8 = var3[var7]; if(var8 == WindowsSelectorImpl.this.wakeupSourceFd) { synchronized(WindowsSelectorImpl.this.interruptLock) { WindowsSelectorImpl.this.interruptTriggered = true; } } else { WindowsSelectorImpl.MapEntry var9 = WindowsSelectorImpl.this.fdMap.get(var8); if(var9 != null) { SelectionKeyImpl var10 = var9.ski; if(!var5 || !(var10.channel() instanceof SocketChannelImpl) || !WindowsSelectorImpl.this.discardUrgentData(var8)) { if(WindowsSelectorImpl.this.selectedKeys.contains(var10)) { if(var9.clearedCount != var1) { if(var10.channel.translateAndSetReadyOps(var4, var10) && var9.updateCount != var1) { var9.updateCount = var1; ++var6; } } else if(var10.channel.translateAndUpdateReadyOps(var4, var10) && var9.updateCount != var1) { var9.updateCount = var1; ++var6; } var9.clearedCount = var1; } else { if(var9.clearedCount != var1) { var10.channel.translateAndSetReadyOps(var4, var10); if((var10.nioReadyOps() & var10.nioInterestOps()) != 0) { WindowsSelectorImpl.this.selectedKeys.add(var10); var9.updateCount = var1; ++var6; } } else { var10.channel.translateAndUpdateReadyOps(var4, var10); if((var10.nioReadyOps() & var10.nioInterestOps()) != 0) { WindowsSelectorImpl.this.selectedKeys.add(var10); var9.updateCount = var1; ++var6; } } var9.clearedCount = var1; } } } } } return var6; }
这里首先根据poll监听到发生io事件所需要处理的fd,根据fd从selector中读取相应的已经注册了的channel,根据所发生的io事件(读,写,异常)更新channel的状态。相应的Selector的select到这里算是结束了。
最后还想看一下WindowsSelectorImpl.wakeup()的具体实现
public Selector wakeup() { Object var1 = this.interruptLock; synchronized(this.interruptLock) { if(!this.interruptTriggered) { this.setWakeupSocket(); this.interruptTriggered = true; } return this; } } private void setWakeupSocket() { this.setWakeupSocket0(this.wakeupSinkFd); } private native void setWakeupSocket0(int var1);
最后找资料,windowsSelectorImpl.c中实现send(scoutFd, (char*)&POLLIN, 1, 0);这儿就跟我们之前selector.open中构造的pipe联系在了一起,向sink端写入了一个自己,注册在pollArray中的wakeupsource端的fd有变化,poll方法返回,并将线程从selector的select中唤醒。
小结
我觉得jdk 的nio的selector底层分析到这里算是完结撒花了,相信看完这几篇后那个图应该算是掌握了。
如果有机会,要去jdk的native方法底层再一探nio的究竟。在jdk层面分析也就到这里结束了。