NIO(四):源码分析_Register部分

  • 注册,就是将当前Channel注册到Selector上,是NIO源码的核心部分

1,Channel.register()部分

  • serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT):注册事件
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自身的注册列表中获取数据
        SelectionKey k = findKey(sel);
        // 获取到数据后,只对SelectionKey状态进行变更
        if (k != null) {
            k.interestOps(ops);
            k.attach(att);
        }
        // key为空,说明该任务为新任务,加锁后注册
        if (k == null) {
            synchronized (keyLock) {
                if (!isOpen())
                    throw new ClosedChannelException();
                k = ((AbstractSelector)sel).register(this, ops, att);
                // 注册完成后,将新注册的SelectionKey添加的Channel的注册列表中
                addKey(k);
            }
        }
        return k;
    }
}
  • AbstractSelectableChannel.findKey():从ChannelSelectionKey[]数组中获取存在数据
private SelectionKey findKey(Selector sel) {
    synchronized (keyLock) {
        if (keys == null)
            return null;
        for (int i = 0; i < keys.length; i++)
            // 一个Channel跟一个Selector是强绑定的,可以通过Selector匹配获取注册的Key
            // Channel和Selector之间是多对多的关系
            if ((keys[i] != null) && (keys[i].selector() == sel))
                return keys[i];
        return null;
    }
}
  • 如果 Channel 的注册列表中不存在该数据,则直接转发到 Selector 进行该 Channel 注册
  • AbstractSelectableChannel.addKey() :注册完成后,添加到 Channel 的注册列表中
private void addKey(SelectionKey k) {
    assert Thread.holdsLock(keyLock);
    int i = 0;
    // 首先数据已经初始化且未满, 正常填充
    if ((keys != null) && (keyCount < keys.length)) {
        for (i = 0; i < keys.length; i++)
            if (keys[i] == null)
                break;
    // 数组未初始化,直接初始化,并制定默认长度3
    } 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++;
}

2,Selector.register()部分

  • ((AbstractSelector)sel).register(this, ops, att):通道未注册到选择器,进行注册
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
    if(!(var1 instanceof SelChImpl)) {
        throw new IllegalSelectorException();
    } else {
        // 初始化一个注册器,封装通道和选择器
        // 生成的对象真是实例为SelectionKeyImpl
        SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
        // 附加各位属性, 会透传到下一个事件
        var4.attach(var3);
        Set var5 = this.publicKeys;
        // 对注册的SelectionKey列表进行同步处理
        synchronized(this.publicKeys) {
            // 初始化完成后,进行最终注册,添加Socket句柄
            this.implRegister(var4);
        }
		// 添加事件类型, 即修改原事件状态
        var4.interestOps(var2);
        return var4;
    }
}

SelectionKeyImpl(SelChImpl var1, SelectorImpl var2) {
    this.channel = var1;
    this.selector = var2;
}
  • WindowsSelectorImpl.implRegister:注册
// 注册
protected void implRegister(SelectionKeyImpl var1) {
    Object var2 = this.closeLock;
    synchronized(this.closeLock) {
        if(this.pollWrapper == null) {
            throw new ClosedSelectorException();
        } else {
            // 对SelectionKeyImpl[] channelArray数组进行二倍扩容, 默认长度为8
            // 没增加1024个Channel,则增加一个线程处理
            this.growIfNeeded();
            // 填充到数据的下一个索引位置
            this.channelArray[this.totalChannels] = var1;
            // 设置SelectionKey的索引值, 添加事件时候会用到
            var1.setIndex(this.totalChannels);
            // SelectionKey句柄和对象的映射
            this.fdMap.put(var1);
            // 添加到选择器的注册列表中
            this.keys.add(var1);
            // 添加Socket句柄到pollWrapper
            // 注册此处传递的总数,也就代表当前注册对象索引
            this.pollWrapper.addEntry(this.totalChannels, var1);
            // 表示已注册的Channel总数
            ++this.totalChannels;
        }
    }
}
  • WindowsSelectorImpl.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);
    }
	// 数量为1024的倍数,对线程加1,select()时作为子线程进行查找事件
    if(this.totalChannels % 1024 == 0) {
        this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, this.totalChannels);
        ++this.totalChannels;
        ++this.threadsCount;
    }

}
  • PollArrayWrapper.addEntry():添加注册对象的Socket句柄到内存对象中
void addEntry(int var1, SelectionKeyImpl var2) {
    this.putDescriptor(var1, var2.channel.getFDVal());
}

void putDescriptor(int var1, int var2) {
    this.pollArray.putInt(SIZE_POLLFD * var1 + 0, var2);
}
  • SelectionKeyImpl.interestOps:修改注册事件
public SelectionKey nioInterestOps(int var1) {
    if((var1 & ~this.channel().validOps()) != 0) {
        throw new IllegalArgumentException();
    } else {
        // 进行注册事件修改
        // 注册事件修改,主要分析ServerSocketChannel和SocketChannel两部分
        this.channel.translateAndSetInterestOps(var1, this);
        this.interestOps = var1;
        return this;
    }
}

// ServerSocketChannel
public void translateAndSetInterestOps(int var1, SelectionKeyImpl var2) {
    int var3 = 0;
    if((var1 & 16) != 0) { // 16 表示accept事件
        var3 |= Net.POLLIN;
    }
	// 对事件进行替换
    var2.selector.putEventOps(var2, var3);
}

// SocketChannel
public void translateAndSetInterestOps(int var1, SelectionKeyImpl var2) {
    int var3 = 0;
    if((var1 & 1) != 0) { // 读事件
        var3 |= Net.POLLIN;
    }

    if((var1 & 4) != 0) { // 写事件
        var3 |= Net.POLLOUT;
    }

    if((var1 & 8) != 0) { // 连接事件
        var3 |= Net.POLLCONN;
    }
	// 对事件类型进行替换
    var2.selector.putEventOps(var2, var3);
}
  • WindowsSelectorImpl.putEventOps:事件替换
public void putEventOps(SelectionKeyImpl var1, int var2) {
    Object var3 = this.closeLock;
    synchronized(this.closeLock) {
        if(this.pollWrapper == null) {
            throw new ClosedSelectorException();
        } else {
            // 从注册对象中获取注册事件的索引位置
            int var4 = var1.getIndex();
            if(var4 == -1) {
                throw new CancelledKeyException();
            } else {
                // 通过内存对象直接操作地址空间,进行事件类型替换
                this.pollWrapper.putEventOps(var4, var2);
            }
        }
    }
}
  • 注册源码分析完成,最终注册事件添加到 SelectorImpl.keys 和 AbstractSelectableChannel.keys,并在 AllocatedNativeObject 内存对象中存储,可以通过 select() 进行查找
这段代码也是用于三维钠池温度分布计算的程序,其中包括了一些变量和参数的声明,如: - Index_Oside_3DCV_INPUT 和 Index_Iside_3DCV_INPUT:用于表示计算区域的内侧和外侧网格编号; - IO_3DPool_X、IO_3DPool_Y 和 IO_3DPool_Z:用于表示输入输出钠池中的位置坐标; - Index_IOside_3DCV_INPUT 和 IOName_Con:用于表示输入输出钠池连接器的名称列表; - IO_3DV:用于表示输入输出的三维空间网格数量; - IO_Cv_X、IO_Cv_Y 和 IO_Cv_Z:用于表示输入输出的计算区域网格数量; - NIO_3Dpool:用于表示输入输出钠池的数量; - Index_IOside_3DCV 和 Connect_IOName:用于表示输入输出钠池的连接器名称; - NX_3DV_Input、NY_3DV_Input 和 NZ_3DV_Input:用于表示输入三维空间的网格数量; - NTOTAL_3DV、NTOTAL_3DJX、NTOTAL_3DJY 和 NTOTAL_3DJZ:用于表示计算区域的总网格数量; - IZ_3DV、IX_3DV 和 IY_3DV:用于表示三维空间中的网格坐标; - IZ_3DJX、IX_3DJX 和 IY_3DJX:用于表示计算区域网格中沿 X 轴的坐标; - IZ_3DJY、IX_3DJY 和 IY_3DJY:用于表示计算区域网格中沿 Y 轴的坐标; - IZ_3DJZ、IX_3DJZ 和 IY_3DJZ:用于表示计算区域网格中沿 Z 轴的坐标; - IX、IY 和 IZ:用于表示网格的坐标; - IJUNC:用于表示节点数量; - IV、ICON、IJX、IJY 和 IJZ:用于表示一些参数。 这些变量和参数的具体含义需要根据程序的实际需求进行理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值