• 选择器理论


    选择器

    选择器提供选择执行已经就绪的任务的能力,这使得多元id成为可能,就绪执行和多元选择使得单线程能够有效的同时管理多个io通道。

    选择器执行分解:

    1.创建一个或者多个可选择的通道。

    2.将这些创建的通道注册到选择器对象中。

    3.选择器会记住开发者关系的通道,它们也会追踪对应的通道是否已经就绪。

    4.开发者调用一个选择器的select方法,当方法从阻塞状态返回时,选择键会被更新。

    5.获取选择键的集合,找到当时已经就绪的通道,通过遍历这些键,开发者可以选择对已就绪的通道做要做的操作。

    选择器:选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。

    可选择通道:这个抽象类提供了实现通道的可选择性所需要的公共方法,它是所有支持就绪检查的通道类的夫妻,FileChannel对象是不可以选择的,因为没有继承SelectableChannel,所有socket通道都是可选择的,包括从pipi对象中获得的通道。SelectableChannel可以

    被注册到Selector对象上,同时可以设定对哪个选择器而言哪种操作是感兴趣的,一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。

    选择键:选择键封装了特定的通道与特定的选择器的注册关系。调用SelectableChannel.register方法会返回选择键并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数形式进行编码),指示了该注册关系所关系的通道操作,以及通道已经准备好的操作。

    建立选择器

    ...
    Selector selector = Selector.open(); 
    channel1.register(selector, SelectionKey.OP_READ); 
    channel2.register(selector, SelectionKey.OP_WRITE); 
    channel3.register(selector, SelectionKey.OP_READ | OP_WRITE); 
    channel4.register(selector, SelectionKey.OP_READ | OP_ACCEPT); 
    ready = selector.select(10000); 
    ...
    

    代码创建了一个新的选择器,然后把这四个socket通道注册到选择器上,而且感兴趣的操作不同,select方法在将线程置于休眠状态直至这些感兴趣的事件中的一个发送或者10秒钟过去,这就是所谓的事件驱动。

    public abstract class Selector
    {
        ...
        public static Selector open() throws IOException;
        public abstract boolean isOpen();
        public abstract void close() throws IOException;
        public abstract SelectionProvider provider();
        ...
    }
    

     selector是通过调用静态工厂方法open来实例化的。通道是调用register方法注册到选择器上的,从代码里面可以看到register方法接受一个Selector对象作为参数,以及一个名为ops的整数型参数,第二个参数表示关心的通道操作。有四种操作:读(read)、写(write)、

    连接(connect)和接受(accept)。并非所有的操作都在所有的选择通道上被支持。比如SocketChannel就不支持accept。

    选择键

    public abstract class SelectionKey
    {
        public static final int OP_READ;
        public static final int OP_WRITE;
        public static final int OP_CONNECT;
        public static final int OP_ACCEPT;
        public abstract SelectableChannel channel();
        public abstract Selector selector();
        public abstract void cancel();
        public abstract boolean isValid();
        public abstract int interestOps();
        public abstract void iterestOps(int ops);
        public abstract int readyOps();
        public final boolean isReadable();
        public final boolean isWritable();
        public final boolean isConnectable();
        public final boolean isAcceptable();
        public final Object attach(Object ob);
        public final Object attachment();
    }
    

    1.一个键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系,channel方法和selector方法反映了这种关系。

    2.开发着可以使用cancel方法终结这种关系,可以使用isValid方法来检查这种有效的关系是否依然存在,可以使用readyOps方法来获取相关的通道已经就绪的操作。

    3.readOps我们往往不需要用,SelectionKey类定义了便于使用的布尔方法来为开发者测试通道的就绪状态。 isWritable()、isReadable()、isConnectable()、isAcceptable()四个方法在任意一个SelectionKey对象上都能安全地调用。

    4.当通道关闭时,所有相关的键会自动取消(一个通道可以被注册到多个选择器上);当选择器关闭时,所有被注册到该选择器的通道都会被注销并且相关的键立即被取消。

    Selector

    public abstract class Selector
    {
        ...
        public abstract Set keys();
        public abstract Set selectedKeys();
        public abstract int select() throws IOException;
        public abstract int select(long timeout) throws IOException;
        public abstract int selectNow() throws IOException;
        public abstract void wakeup();
        ...   
    }
    

     每个Selector对象维护三种键的集合:

    已注册的键的集合:与选择器关联的已经注册的键的集合,并不是所有注册过的键都有效,这个集合通过keys方法返回,可能是空的,这些键的结合不可以直接修改。

    已选择的键的集合:已注册的键的集合的子集,这个集合每个成员都是相关的通道被选择器判断为已经准备好的并且包含于键的interest集合中的操作,这个结合通过selectedKeys返回(有可能是空的)。

    已取消的键的集合:已注册的键的集合的子集,这个集合包含了cancel方法被调用过的键(这个键已经被无效化了),但是它们还没有被注销。这个集合是选择器对象的私有成员,因而无法直接访问。

    选择过程

    基本上说,选择器是对select,poll,epoll等本地调用或者类似的操作系统特定的系统调用的包装。选择操作是当三种形式的select中的任意一种被调用时,由选择器执行。不管哪一种形式的调用,下面步骤都要执行:

    1、已取消的键的集合将会被检查。如果它是非空的,每个已取消的键的集合中的键将从另外两个集合中移除,并且相关的通道将被注销。此步骤结束,已取消的键的集合将是空的。

    2、已注册的键的集合中的键的interest集合将被检查,此步骤结束,对interest集合的改动不会影响剩余的检查过程。一旦就绪条件被定下来,底层操作系统将会进行查询,以确定每个通道所关心的操作的真实就绪状态,依赖于特定的select()方法调用,如果没有通道已经准备好,线程可能会在这时阻塞,通常会有一个超时值。

    3、步骤2可能会花费很长时间,特别是线程处于阻塞状态时。与该选择器相关的键可能会同时被取消,当步骤2结束时,步骤1将重新执行,以完成任意一个在选择进行的过程中,键已经被取消的通道的注册。

    4、select操作的返回值不是已准备好的通道的总数,而是从上一个select()调用之后进入就绪状态的通道的数量。之前的调用中就绪的,并且在本次调用中仍然就绪的通道不会被计入,而那些在前一次调用中已经就绪但已经不再处于就绪状态的通道也不会被计入。

    最后,上面的Selector中还有两个方法没有提到,这里说明一下它们的意思:

    1、selectNow()

    调用selectNow()方法执行就绪检查过程,但不阻塞,如果当前没有通道就绪,立刻返回0.

    2、wakeup()

    调用wakeup()方法将使得选择器上的第一个还没有返回的选择操作立即返回,如果当前没有正在进行中的选择,那么下一次对select()方法的一种形式的调用将立即返回,后续的选择操作将正常进行。

  • 相关阅读:
    设计模式
    idea多个项目
    多个tomcat配置
    mysql数据库默认时间字段格式
    读取文件
    上传图片
    数据库创建用户授权
    统计12个月份的数据
    行列转换
    分页
  • 原文地址:https://www.cnblogs.com/tp123/p/6439690.html
Copyright © 2020-2023  润新知