使用选择器

来源:互联网 发布:mac. 压缩软件 编辑:程序博客网 时间:2024/05/16 02:08

选择过程 

    选择器维护着注册过的通道的集合,并且这些注册关系中的任意一个都是封装在SelectionKey对象中的。每一个Selector 对象维护三个键的集合。

public abstract class Selector {     // This is a partial API listing     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( ); } 

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

    已选择的键的集合(Selected key set):已注册的键的集合的子集。这个集合的每个成员都是相关的通道被选择器(在前一个选择操作中)判断为已经准备好的。这个集合通过selectedKeys( )方法返回(并有可能是空的)。这是一个键的集合,每个键都关联一个已经准备好至少一种操作的通道。每个键都有一个内嵌的ready集合,指示了所关联的通道已经准备好的操作。

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

    在一个刚初始化的Selector 对象中,这三个集合都是空的。

    Selector 类的核心选择过程基本上来说,选择器是对select( ) poll( ) 等本地调用(native call) 或者类似的操作系统特定的系统调用的一个包装。选择操作是当三种形式的select( ) 中的任意一种被调用时,由选择器执行的。不管是哪一种形式的调用,下面步骤将被执行:

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

     2 已注册的键的集合中的键的interest 集合将被检查。检查执行过后,对interest集合的改动不会影响剩余的检查过程。一旦就绪条件被定下来,底层操作系统会进行查询,以确定每个通道所关心的真实就绪状态,依赖于特定的select()方法调用,如果没有通道已经准备好,线程可能会在这时阻塞,通常会有超时。对于那些操作系统指示至少已经准备好interest 集合中的一种操作的通道,将执行以下两种操作中的一种:如果通道的键还没有处于已选择的键的集合中,那么键的ready集合将被清空,然后表示操作系统发现的当前通道已经准备好的操作的比特掩码将被设置。 否则,也就是键在已选择的键的集合中。键的ready集合将被表示操作系统发现的当前已经准备好的操作的比特掩码更新。所有之前的已经不再是就绪状态的操作不会被清除。事实上,所有的比特位都不会被清理。一旦键被放置于选择器的已选择的键的集合中,它的ready集合将是累积的。比特位只会被设置,不会被清理。

     3 select操作返回的值是ready集合在步骤中被修改的键的数量,而不是已选择的键的集合中的通道的总数。返回值不是已准备好的通道的总数,而是从上一个selec t( ) 调用之后进入就绪状态的通道的数量。

     selectNow() 方法执行就绪检查过程,但不阻塞。如果当前没有通道就绪,它将立即返回 0

停止选择过程   

   Selector的API中的最后一个方法,wakeup( ),提供了使线程从被阻塞的select( ) 方法中优雅地退出的能力。有三种方式可以唤醒在select( )方法中睡眠的线程。

   调用wakeup( ):调用Selector 对象的wakeup( )方法将使得选择器上的第一个还没有返回的选择操作立即返回。您可能只想唤醒一个睡眠中的线程,而使得后续的选择继续正常地进行。您可以通过在调用wakeup( )方法后调用selectNow( )方法来绕过这个问题。

   调用close( ):选择器的close( )方法被调用,那么任何一个在选择操作中阻塞的线程都将被唤醒,就像wakeup( ) 方法被调用了一样。与选择器相关的通道将被注销,而键将被取消。

   调用interrupt( ):睡眠中的线程的interrupt( ) 方法被调用,它的返回状态将被设置。

管理选择键

    选择是累积的。一旦一个选择器将一个键添加到它的已选择的键的集合中,它就不会移除这个键。并且,一旦一个键处于已选择的键的集合中,这个键的ready集合将只会被设置,而不会被清理。

    当通道上的至少一个感兴趣的操作就绪时,键的ready集合就会被清空,并且当前已经就绪的操作将会被添加到ready集合中。该键之后将被添加到已选择的键的集合中。

    清理一个SelectKeyready集合的方式是将这个键从已选择的键的集合中移除。选择键的就绪状态只有在选择器对象在选择操作过程中才会修改。处理思想是只有在已选择的键的集合中的键才被认为是包含了合法的就绪信息的。这些信息将在键中长久地存在,直到键从已选择的键的集合中移除,以通知选择器您已经看到并对它进行了处理。如果下一次通道的一些感兴趣的操作发生时,键将被重新设置以反映当时通道的状态并再次被添加到已选择的键的集合中。

    这种框架提供了很多灵活性。通常的做法是在选择器上调用一次select操作(这将更新已选择的键的集合,然后遍历selectKeys( )方法返回的键的集合。在按顺序进行检查每个键的过程中,相关的通道也根据键的就绪集合进行处理。然后键将从已选择的键的集合中被移除(通过在Iterator对象上调用remove(  ) 方法),然后检查下一个键。完成后,通过再次调用select( ) 方法重复这个循环。

并发性

    选择器对象是线程安全的,但它们包含的键集合不是。通过keys( ) selectKeys( )返回的键的集合是Selector对象内部的私有的Set对象集合的直接引用。这些集合可能在任意时间被改变。

    多个线程并发地访问一个选择器的键的集合,采取一些步骤来合理地同步访问:在执行选择操作时,选择器在Selector 对象上进行同步,然后是已注册的键的集合,最后是已选择的键的集合,按照这样的顺序。

原创粉丝点击