NIO 之 Selector实现原理

作者: jijs | 来源:发表于2017-08-20 01:04 被阅读484次

    相关文章

    NIO 之 ByteBuffer实现原理
    NIO 之 Channel实现原理
    NIO 之 Selector实现原理

    概述

    Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

    这是在一个单线程中使用一个Selector处理3个Channel的图示:


    selector与channel关系

    要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

    Selector 作用

    仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源(如内存)。因此,使用的线程越少越好。
    Selector能够在单个线程中处理多个通道,这样可以减少多个线程造成上下文切换问题。

    Selector 源码分析

    public abstract class Selector implements Closeable {
        protected Selector() { }
        public static Selector open() throws IOException {
            return SelectorProvider.provider().openSelector();
        }
        public abstract boolean isOpen();
        public abstract SelectorProvider provider();
        public abstract Set<SelectionKey> keys();
        public abstract Set<SelectionKey> selectedKeys();
        public abstract int selectNow() throws IOException;
        public abstract int select(long timeout) throws IOException;
        public abstract int select() throws IOException;
        public abstract Selector wakeup();
        public abstract void close() throws IOException;
    

    Selector 是个抽象类,提供一个静态的方法获取Selector子类SelectorImpl的实例。

    下面分析Selector的几个方法

    register 方法

    该方法是在 Channel的register方法中调用的。具体详见NIO 之 Channel实现原理

    register 方法
    1. 通过channel和selector构造一个SelectionKey的实例。
    2. SelectionKey 注册感兴趣的事件

    这四种事件用SelectionKey的四个常量来表示:
    SelectionKey.OP_CONNECT
    SelectionKey.OP_ACCEPT
    SelectionKey.OP_READ
    SelectionKey.OP_WRITE

    select 方法

    不同的 Channel 注册到 Selector 后,就可以随时查询 Selector ,找出哪些 Channel 已经准备好可以进行处理。Channel 可能准备好上面注册到 Selector 感兴趣事件中的一个或多个。

    1. select()
      获取就绪的 Channel,阻塞方法,没有就绪的 Channel 就一直阻塞该线程。
    public int select() throws IOException {
        return select(0);
    }
    
    1. select(long timeout)
      获取就绪的 Channel, 阻塞方法,阻塞 timeout 时间,如果超时还没有就绪的 Channel,返回0,不做任何操作。
    public int select(long timeout)
            throws IOException
        {
            if (timeout < 0)
                throw new IllegalArgumentException("Negative timeout");
            return lockAndDoSelect((timeout == 0) ? -1 : timeout);
        }
    
    1. selectNow()
      获取就绪的 Channel,如果没有就绪的就直接返回,不阻塞当前线程。
    public int selectNow() throws IOException {
            return lockAndDoSelect(0);
        }
    

    上面三个 select方法底层都是调用 lockAndDoSelect 方法。
    lockAndDoSelect方法的参数值 说明:
    -1 : 一直阻塞,直到有就绪的 Channel 可处理
    0 : 不阻塞
    0: 表示阻塞多长时间

    keys 方法

    获取所有注册到 Selector 上的 SelectionKey
     public Set<SelectionKey> keys() {
        if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException();
            return publicKeys;
    }
    

    selectedKeys 方法

    获取所有注册到 Selector 上就绪 Channel 的 SelectionKey 信息。

    public Set<SelectionKey> selectedKeys() {
        if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException();
            return publicSelectedKeys;
    }
    

    SelectionKey 解析

    SelectionKey 类结构如下:

    public abstract class SelectionKey {
        protected SelectionKey() { }
        public static final int OP_READ = 1 << 0;
        public static final int OP_WRITE = 1 << 2;
        public static final int OP_CONNECT = 1 << 3;
        public static final int OP_ACCEPT = 1 << 4;
        //附件信息
        private volatile Object attachment = null;
        ....
    }
    
    • public abstract SelectableChannel channel()
      获取channel对象

    • public abstract Selector selector()
      获取seletor对象

    • public abstract void cancel()
      从 Selector 中取消注册该Channel

    • public abstract int interestOps()
      获取该chennel 注册到 selector 上的事件

    • public abstract SelectionKey interestOps(int ops)
      修改注册到 selector 上的事件

    • public abstract int readyOps()
      是否读就绪

    读就绪不等于可读,如果没有注册读事件是不能读的。

    • public final boolean isReadable()
      判断是否可读

    • public final boolean isWritable()
      是否可写

    • public final boolean isConnectable()
      是否已经连接

    • public final Object attach(Object ob)
      添加附件信息

    • public final Object attachment()
      获取附件信息


    想了解更多精彩内容请关注我的公众号

    相关文章

      网友评论

        本文标题:NIO 之 Selector实现原理

        本文链接:https://www.haomeiwen.com/subject/drmtdxtx.html