java NIO基于selector实现。
首先创建一个Selector.
Selector selector = Selector.open();
Selector.open的实现.
public static Selector open() throws IOException {
//通过SelectorProvider来开启selector
return SelectorProvider.provider().openSelector();
}
SelectorProvider.provider()会根据 JDK 的类型(linux,windows,macOs),返回对应的SelectorProvider的实现类,在macOs上是KQueueSelectorProvider,并且SelectorProvider是单例模式,后续整个jvm中都使用这个一个KQueueSelectorProvider.
java.nio.channels.spi.SelectorProvider#provider
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
//是否通过启动参数来配置selectorProvider的实现类
if (loadProviderFromProperty())
return provider;
//是否通过SPI来加载指定的selectorProvider实现类
if (loadProviderAsService())
return provider;
//返回KQueueSelectorProvider
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
KQueueSelectorProvider的openSelector会创建KQueueSelectorImpl,这个是Selector的实现类。
KQueueSelectorImpl中主要关注几个点.
- 父类AbstractSelector的cancelledKeys字段,保存被取消的selectionKey,用于后续清理使用。
- 父类SelectorImpl的keys字段,保存所有注册的selectionKey,还有selectedKeys字段,用于selector被唤醒后所有可操作的selectionKey.
- kqueueWrapper字段,nio实现事件监听的核心,用于监听本地的文件变化。
- fdMap字段,维护fd和SelectionKey的映射关于,后续事件来了需要通过fd的编号找到对应的SelectionKey.
这里说一下KqueueWrapper类,其中keventArray是获取nio事件的核心,AllocatedNativeObject中持有nio event的地址,nio有事件变化后,能够通过它获取到nio的事件变化,以及事件的类型。
Selector创建好之后,SelectableChannel就会去注册监听,相关逻辑。
java.nio.channels.spi.AbstractSelectableChannel#register
public final SelectionKey register(Selector sel, int ops,
Object att)
throws ClosedChannelException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (blocking)
throw new IllegalBlockingModeException();
//如果之前这个channel已经对这个selector监听过的话,只需要更新selectionKey的信息
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
//实际是selector去注册这个channel
k = ((AbstractSelector)sel).register(this, ops, att);
addKey(k);
}
}
return k;
}
}
selector.register逻辑.
sun.nio.ch.SelectorImpl#register
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
if (!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
//创建一个SelectionKey, 其中维护了channel和selector的信息。
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
var4.attach(var3);
synchronized(this.publicKeys) {
//真正去注册
this.implRegister(var4);
}
var4.interestOps(var2);
return var4;
}
}
sun.nio.ch.KQueueSelectorImpl#implRegister
protected void implRegister(SelectionKeyImpl var1) {
if (this.closed) {
throw new ClosedSelectorException();
} else {
int var2 = IOUtil.fdVal(var1.channel.getFD());
//把当前channel对应的fd值SelectionKey的对应关系维护起来,用于后续selector通知用。
this.fdMap.put(var2, new KQueueSelectorImpl.MapEntry(var1));
++this.totalChannels;
//加到keys中
this.keys.add(var1);
}
}
下面就是Selector.selector的逻辑,nio的具体核心实现就在这里面。
sun.nio.ch.KQueueSelectorImpl#doSelect
protected int doSelect(long var1) throws IOException {
boolean var3 = false;
if (this.closed) {
throw new ClosedSelectorException();
} else {
//处理被cancel的key, 删除文件具柄
this.processDeregisterQueue();
int var7;
try {
this.begin();
//selector的核心,返回当前活跃的事件数,如果var=-1的话,会一直阻塞,直到有事件变化进来。
var7 = this.kqueueWrapper.poll(var1);
} finally {
this.end();
}
//处理被cancel的key, 删除文件具柄
this.processDeregisterQueue();
//也是selector的核心,具体看内部实现
return this.updateSelectedKeys(var7);
}
}
sun.nio.ch.KQueueSelectorImpl#updateSelectedKeys
private int updateSelectedKeys(int var1) throws IOException {
int var2 = 0;
boolean var3 = false;
++this.updateCount;
for(int var4 = 0; var4 < var1; ++var4) {
//核心kqueueWrapper,获取本次nio事件的fd值
int var5 = this.kqueueWrapper.getDescriptor(var4);
if (var5 == this.fd0) {
var3 = true;
} else {
//通过fd与selectionKey的映射拿到selectionKey
KQueueSelectorImpl.MapEntry var6 = (KQueueSelectorImpl.MapEntry)this.fdMap.get(var5);
if (var6 != null) {
//核心kqueueWrapper,获取本次nio事件的类型
int var7 = this.kqueueWrapper.getReventOps(var4);
SelectionKeyImpl var8 = var6.ski;
if (this.selectedKeys.contains(var8)) {
//如果当前selectedKeys已经存在话,只更新SelectionKey的readyOps
if (var6.updateCount != this.updateCount) {
if (var8.channel.translateAndSetReadyOps(var7, var8)) {
++var2;
var6.updateCount = this.updateCount;
}
} else {
//同一个SelectionKey,没有发生变化
var8.channel.translateAndUpdateReadyOps(var7, var8);
}
} else {
//更新SelectionKey的readyOps
var8.channel.translateAndSetReadyOps(var7, var8);
if ((var8.nioReadyOps() & var8.nioInterestOps()) != 0) {
//把selectionKey加入到selectedKeys中
this.selectedKeys.add(var8);
//增加本次nio事件的个数
++var2;
var6.updateCount = this.updateCount;
}
}
}
}
}
if (var3) {
synchronized(this.interruptLock) {
IOUtil.drain(this.fd0);
this.interruptTriggered = false;
}
}
//返回本次nio事件的个数
return var2;
}
这样selector.select返回后,就可以通过selectedKey去拿到本次发生事件变化的selectionKey了。再去对selectionKey绑定的channel进行操作。
2913

被折叠的 条评论
为什么被折叠?



