jdk中nio源码解析

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中主要关注几个点.

  1. 父类AbstractSelector的cancelledKeys字段,保存被取消的selectionKey,用于后续清理使用。
  2. 父类SelectorImpl的keys字段,保存所有注册的selectionKey,还有selectedKeys字段,用于selector被唤醒后所有可操作的selectionKey.
  3. kqueueWrapper字段,nio实现事件监听的核心,用于监听本地的文件变化。
  4. 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进行操作。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值