• Selector学习笔记 (未完待续)


    Selector open流程

    Selector.open()

    可以看到Selector是由Selector.provider()提供的provider提供的.所以接下来看看SelectorProvider::provider方法

        public static Selector open() throws IOException {
            return SelectorProvider.provider().openSelector();
        }
    

    SelectorProvider.provider()

    windows这边基本上都是走WindowsSelectorProvider了.另一种linux的还没看.

        public static SelectorProvider provider() {
            synchronized (lock) {
                if (provider != null)
                    return provider;
                return AccessController.doPrivileged(
                    new PrivilegedAction<SelectorProvider>() {
                        public SelectorProvider run() {
                                //从系统环境变量中"java.nio.channels.spi.SelectorProvider"加载Provider实现.
                                if (loadProviderFromProperty())
                                    return provider;
                                //通过JAVA SPI加载Provider的实现
                                if (loadProviderAsService())
                                    return provider;
                                //默认Provider,我看win版jdk里返回的是WindowsSelectorProvider
                                provider = sun.nio.ch.DefaultSelectorProvider.create();
                                return provider;
                            }
                        });
            }
        }
    

    WindowsSelectorProvider.openSelector()

    返回WindowsSelectorImpl.java

        public AbstractSelector openSelector() throws IOException {
            return new WindowsSelectorImpl(this);
        }
    

    register流程

    AbstractSelector::register

    f011ff142044396a3c8d27bea1e5c58a.png
    如上图所示,首先只有SelectableChannel可以注册至selector上,而我们常用到的大部分channel基本都继承自SelectableChannel.
    接下来看看AbstractSelectableChannel::register的API介绍

    使用给定的Selector注册此Channel,并返回SelectionKey。
    如果此Channel已在给定的Selector中注册,则将其兴趣(ops参数)设置为给定值后,返回表示注册的SelectionKey.
    若此Channel尚未在给定的Selector中注册,则在保持适当的锁的同时调用AbstractSelector::register方法进行注册.返回key之前先添加进channel的key集合中.(每个channel也维护了自己注册过的selector集合)

        public final SelectionKey register(Selector sel, int ops,
                                           Object att) throws ClosedChannelException
        {
            synchronized (regLock) {
                //验证通道是否打开
                if (!isOpen())
                    throw new ClosedChannelException();
                //validOps返回子类支持的ops,即对应的位数值为1. 此处校验ops是否有支持权限外的位数值为1
                if ((ops & ~validOps()) != 0)
                    throw new IllegalArgumentException();
                //selector不支持阻塞channel注册.此处检查了channel是否为阻塞模式.
                if (blocking)
                    throw new IllegalBlockingModeException();
                //检查此channel是否已经注册过Selector了.
                SelectionKey k = findKey(sel);
                //如果注册过,就更新ops和attachment
                if (k != null) {
                    k.interestOps(ops);
                    k.attach(att);
                }
                //如果没有注册过
                if (k == null) {
                    // New registration
                    synchronized (keyLock) {
                        //检查连接是否是打开的
                        if (!isOpen())
                            throw new ClosedChannelException();
                        //走selector的register方法注册此channel.
                        k = ((AbstractSelector)sel).register(this, ops, att);
                        //并添加至此channel记录的注册的Selector集合.
                        addKey(k);
                    }
                }
                return k;
            }
        }
    

    看下来,发现channel最后还是通过调用Selector::register方法进行注册,所以接下来看Selector的register方法.

    SelectorImpl::register

    b1a6d0f201886bb990380e00f425e6e1.png
    SelectorImpl实现了Selector的register接口.SelectionKey的创建是在这一步进行的.

        protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
            //只允许SelChImpl实现类可以注册.
            if (!(var1 instanceof SelChImpl)) {
                throw new IllegalSelectorException();
            } else {
                //创建SelectionKey并返回.
                SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
                var4.attach(var3);
                synchronized(this.publicKeys) {
                    //注册到Selector实现里.
                    this.implRegister(var4);
                }
                var4.interestOps(var2);
                return var4;
            }
        }
    

    WindowsSelectorImpl::implRegister

    windows下的JDK8默认实现是这个. 总的来说selector维护了一批SelectionKey.

        //从SelectorImpl下继承来的.
        protected HashSet<SelectionKey> keys = new HashSet();
        //一个存放 selectionKey的Map(继承于HashMap)
        private final WindowsSelectorImpl.FdMap fdMap = new 
    WindowsSelectorImpl.FdMap();
        //一个用于存放SelectionKey的数组. 此处自己用数组实现增删可能是为了快?
        private SelectionKeyImpl[] channelArray = new 
    SelectionKeyImpl[8];
        private int totalChannels = 1;
    
        protected void implRegister(SelectionKeyImpl var1) {
            synchronized(this.closeLock) {
                if (this.pollWrapper == null) {
                    throw new ClosedSelectorException();
                } else {
                    //判断数组是否需要扩容
                    this.growIfNeeded();
                    //追加至channelArray数组的末尾.
                    this.channelArray[this.totalChannels] = var1;
                    //并且selectionKey自己也记录着自己在selector中的哪个位置.
                    var1.setIndex(this.totalChannels);
                    //往map集合里存一个
                    this.fdMap.put(var1);
                    //往set集合里存一个
                    this.keys.add(var1);
                    //存放文件句柄信息( fd ( file description ))
                    this.pollWrapper.addEntry(this.totalChannels, var1);
                    ++this.totalChannels;
                }
            }
        }
    

    select流程

    SelectorImpl.doSelect(long timeout)

    WindowsSelectorImpl::doSelect

    windows JDK8 默认自带的Selector的doSelect()方法.

        
        //存放着文件句柄,基本用unsafe直接操作内存.
        private PollArrayWrapper pollWrapper = new PollArrayWrapper(8);
    
        protected int doSelect(long var1) throws IOException {
            if (this.channelArray == null) {
                throw new ClosedSelectorException();
            } else {
                this.timeout = var1;
                //清除注销队列(有个集合记录了cancel的key.通过这个方法将其正式清除)
                this.processDeregisterQueue();
                if (this.interruptTriggered) {
                    this.resetWakeupSocket();
                    return 0;
                } else {
                    //启动/销毁WindowsSelectorImpl.SelectThread线程,保证有threadsCount(每增加1024个连接,这个值+1)个线程在工作.每个SelectThread都有一个SubSelector去poll这个Selector里的PollArrayWrapper里的句柄.  *推测*  并发分段轮询机制.
                    this.adjustThreadsCount();
                    this.finishLock.reset();
                    this.startLock.startThreads();
    
                    try {
                        this.begin();
    
                        try {
                            //负责一开始1024个channel的poll工作.后面的每1024个连接都将给SelectThread的SubSelector负责poll. 据说系统调用的poll会因为句柄太多影响性能,所以进行拆分.(待证)
                            this.subSelector.poll();
                        } catch (IOException var7) {
                            this.finishLock.setException(var7);
                        }
    
                        if (this.threads.size() > 0) {
                            this.finishLock.waitForHelperThreads();
                        }
                    } finally {
                        this.end();
                    }
    
                    this.finishLock.checkForException();
                    //清除Cancel了的连接.
                    this.processDeregisterQueue();
                    //更新keys事件. 这个方法的核心在于SubSelector的processSelectedKeys()方法
                    int var3 = this.updateSelectedKeys();
                    this.resetWakeupSocket();
                    return var3;
                }
            }
        }
    
  • 相关阅读:
    PHP WEB项目文件夹上传下载解决方案
    .NET WEB项目文件夹上传下载解决方案
    C#.NET WEB项目文件夹上传下载解决方案
    python 多重继承
    python 多态
    python 类型判断-- isinstance函数
    python 继承
    hdu 5692 Snacks 线段树+dfs
    线段树的输出
    python 定义类方法
  • 原文地址:https://www.cnblogs.com/jiangxiewei/p/13280863.html
Copyright © 2020-2023  润新知