Selector创建完毕,紧接着看Channel的注册。
Selector中的channel注册需要SelectableChannel调用register()方法开始进行注册流程,具体的register()方法在AbstractSelectableChannel中实现。
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();
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();
k = ((AbstractSelector)sel).register(this, ops, att);
addKey(k);
}
}
return k;
}
}
在channel中,将会有一个数组来保存已经注册过的SelectionKey,一个SelectionKey代表一个Channel和一个selector的注册关系。注册时首先,通过findKey()寻找key判断这个channel是否已经注册过。如果这个channel是第一次进行注册操作,显然,这里肯定是找不到的。具体findKey()方法实现如下:
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;
}
}
这里如果一开始找到了SelectorKey(对应的key),说明该channel与传递进来的selector已经注册过,那么只需要更新interestops和attach。
如果没有找到SelectorKey,就需要对传进来的Selector进行注册。Selector的regist()方法实现在SelectorImpl中:
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
if(!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
var4.attach(var3);
Set var5 = this.publicKeys;
synchronized(this.publicKeys) {
this.implRegister(var4);
}
var4.interestOps(var2);
return var4;
}
}
首先根据channel与selector生成对应SelectionKeyImpl,其构造方法只保存了传递进来的参数。之后调用子类的ImplRegister()方法,这里以WindowsSelectorImpl为例子
protected void implRegister(SelectionKeyImpl var1) {
Object var2 = this.closeLock;
synchronized(this.closeLock) {
if(this.pollWrapper == null) {
throw new ClosedSelectorException();
} else {
this.growIfNeeded();
this.channelArray[this.totalChannels] = var1;
var1.setIndex(this.totalChannels);
this.fdMap.put(var1);
this.keys.add(var1);
this.pollWrapper.addEntry(this.totalChannels, var1);
++this.totalChannels;
}
}
}
在这里是对SelectionKeyImpl进行进一步加工,以完成其对注册关系抽象的功能。
这里由于传入新注册的SelectionKey,所以在growIfNeeded()方法中,如果数组长度已经不够,就需要对数组进行扩容:
private void growIfNeeded() {
if(this.channelArray.length == this.totalChannels) {
int var1 = this.totalChannels * 2;
SelectionKeyImpl[] var2 = new SelectionKeyImpl[var1];
System.arraycopy(this.channelArray, 1, var2, 1, this.totalChannels - 1);
this.channelArray = var2;
this.pollWrapper.grow(var1);
}
if(this.totalChannels % 1024 == 0) {
this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, this.totalChannels);
++this.totalChannels;
++this.threadsCount;
}
}
接着根据当前channel数量将相应点的channel存放在数组相应的下标下的位置,并在SelectionKey中保存在被注册的selector中的位置索引。之后将在put()方法中完成所注册的channel的FDVal与具体的SelectionKey的键值对的映射。
private WindowsSelectorImpl.MapEntry put(SelectionKeyImpl var1) {
return (WindowsSelectorImpl.MapEntry)this.put(new Integer(var1.channel.getFDVal()), new WindowsSelectorImpl.MapEntry(var1));
}
void addEntry(int var1, SelectionKeyImpl var2) {
this.putDescriptor(var1, var2.channel.getFDVal());
}
fdMap以channel的fdVal为key,与具体的SelectionKey为值的键值对。
接着将将该SelectionKey与当时的channel数量作为键值对保存在pollWrapper中,最后自增channel数。
当SelectionKey经过Selector的产生后边,将会返回给channel,这个时候通过addKey()将刚刚产生的key添加到channel中。
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++;
}
在addKey()方法中,如果此时大小还足够,那么可以直接在相应的位置顺序将SelectionKey保存,如果需要扩容,则先扩容,在扩容之后保存相应的SelectionKey。