jdk的selector(2)channel的注册

Selector中的channel注册需要由SelectabelChannel调用其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;
    }
}

 

如果一开始就找到了相应的key,说明该channel与传递进来需要注册的selctor已经注册过,那么就是对相应的interestOps和attach进行更新,并没有新的注册操作。

 

但是,如果没有找到相应的SelectionKey,就说明需要与传递进来的Selector进行注册操作。

Selector的register()方法实现在了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,其构造方法就是简单的保存了传递进来的参数,并且将所需要attach的object进行保存。之后,调用子类的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));
}

 

最后,将该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。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值