今天给本身的项目测出了个bug,NIOServer端在读取一次数据后我一开始是选择将获取的迭代器remove掉,可是发现selectNow()中获取的SelectionKey还存在,后来将其直接SelectionKey.cancel();后就只能传递一次数据了。这是因为我只是初步接触NIO没有深刻了解每一个模块的具体实现致使的,下面我经过阅读源码来了解SelectionKey和Channel、Selector这三者的关系来梳理相关知识。
复制代码
因为Channel只是接口,所以继续获取该方法的实现,最终定位到AbstractSelectableChannel类,为了方便阅读以及符合主题,我省略了部分无关代码,相关实现以下:java
public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException {
synchronized (regLock) {
/* 异常状态检查代码,省略 */
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
synchronized (keyLock) {
/* 异常状态检查代码,省略 */
k = ((AbstractSelector)sel).register(this, ops, att);
addKey(k);
}
}
return k;
}
}
复制代码
那么这里能够知道当Channel往Selector注册的时候Channel会先调用本地方法findKey传入要注册的Selector获取对应的SelectionKey,而参数只有Selector,没有相关状态,所以这里就能够直接知道SelectionKey对应的就是一个Selector,我前面将其直接cancel掉的话其实是将这个Channel从对应的Selector中取消注册了,这就不免在同一个链接中只能传递一次,由于在等待下次IO的时候我已经将其从Selector中取消了读状态。数组
相应的,若是SelectionKey不为null,则表示该Channel已经在目标Selector上注册,所以只要将目标状态添加进SelectionKey中便可。安全
若是SelectionKey为null,则直接由Selector的注册方法建立并添加进Channel中。this
那么继续查看这两个方法,首先是addKeyspa
private void addKey(SelectionKey k) {
assert Thread.holdsLock(keyLock);
int i = 0;
if ((keys != null) && (keyCount < keys.length)) {
// Find empty element of key array
for (i = 0; i < keys.length; i++)
if (keys[i] == null)
break;
} else if (keys == null) {
keys = new SelectionKey[3];
} else {
// Grow key array
int n = keys.length * 2;
SelectionKey[] ks = new SelectionKey[n];
for (i = 0; i < keys.length; i++)
ks[i] = keys[i];
keys = ks;
i = keyCount;
}
keys[i] = k;
keyCount++;
}
复制代码
这段代码十分简单,忽略相关安全检查后能够看到这个操做主要是对keys的操做,这个keys经过查看源码能够看到它是一个SelectionKey数组用以保存该Channel所绑定的Selector生成的SelectionKey对象。它默认长度为3,每次进行扩容将其长度*2.rest
那么findKey就很好猜想它的实现了:应该是对这个keys数组的遍历,并将其中的SelectionKey与Selector对比便可.code
private SelectionKey findKey(Selector sel) {
synchronized (keyLock) {
if (keys == null)
return null;
for (int i = 0; i < keys.length; i++)
if ((keys[i] != null) && (keys[i].selector() == sel))
return keys[i];
return null;
}
}
复制代码
既然有find和and了那天然就会有removecdn
void removeKey(SelectionKey k) { // package-private
synchronized (keyLock) {
for (int i = 0; i < keys.length; i++)
if (keys[i] == k) {
keys[i] = null;
keyCount--;
}
((AbstractSelectionKey)k).invalidate();
}
}
复制代码
Channel、Selector经过一个惟一的SelectionKey实现注册。对象