Java Nio 十八

2018/01/24 Java

Java NIO 十八

选择器(selectors)

选择器提供选择执行已经就绪的任务的能力,这使得 多元 I/O 成为可能。就绪选择和多元执行使得单线程能够有效率地同 时管理多个 I/O 通道(channels)。就绪选择功能直到 JDK 1.4 才成为可行的方案。

选择器基础

选择器提供了询问通道是否已经准备好执行每个 I/0 操作的能力。在与 SelectableChannel 联合使用时,选择器提供了这种服务,但这里面有更多的事情需要去了解。就绪选择的真正价值在于潜在的大量的通道可以同时进行就绪状态的检查。

有两种方式可以选择:被激发的线程可以处于休眠状态,直到一个或者多个注册到选择器的通道就绪,或者它也可以周期性地轮询选择器,看看从上次检查之后,是否有通道处于就绪状态。

真正的就绪选择必须由操作系统来做。操作系统的一项最重要的功能就是处理 I/O 请求并通知各个线程它们的数据已经准备好了。选择器类提供了这种抽象,使得 Java 代码能够以可移植的方式,请求底层的操作系统提供就绪选择服务。

选择器,可选择通道和选择键类

  • 选择器(Selector)

选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。当这么做的时候,可以选择将被激发的线程挂起,直到有就绪的的通道。

  • 可选择通道(SelectableChannel)

这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。FileChannel 对象不是可选择的,因为它们没有继承 SelectableChannel。 所有 socket 通道都是可选择的,包括从管道(Pipe)对象的中获得的通道。SelectableChannel 可以被注册到 Selector 对象上,同时可以指定对那个选择器而言,那种操作是感兴趣的。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。

  • 选择键(SelectionKey)

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

就绪相关类的继承关系图: 图一

部分API 如下:


public abstract class SelectableChannel
        extends AbstractChannel
        implements Channel
{
        // This is a partial API listing
        public abstract SelectionKey register (Selector sel, int ops)
        throws ClosedChannelException;
        public abstract SelectionKey register (Selector sel, int ops,
        Object att)
        throws ClosedChannelException;
        public abstract boolean isRegistered( );

        public abstract SelectionKey keyFor (Selector sel);
        public abstract int validOps( );
        public abstract void configureBlocking (boolean block)
        throws IOException;
        public abstract boolean isBlocking( );
        public abstract Object blockingLock( );
}

通道在被注册到一个选择器上之前,必须先设置为非阻塞模式(通过调用 configureBlocking(false))

如下图: 图三

调用可选择通道的 register( )方法会将它注册到一个选择器上。试图注册一个处于阻塞状态的通道,register( )将抛出未检查的 IllegalBlockingModeException 异常。通道一旦被注册,就不能回到阻塞状态。试图这么做的话,将在调用 configureBlocking( )方法时将抛出 IllegalBlockingModeException 异常。

试图注册一个已经关闭的 SelectableChannel 实例的话,也将抛出 ClosedChannelException 异常。

Selector类的API如下:


public abstract class Selector
{
        public static Selector open( ) throws IOException
        public abstract boolean isOpen( );
        public abstract void close( ) throws IOException;
        public abstract SelectionProvider provider( );
        public abstract int select( ) throws IOException;
        public abstract int select (long timeout) throws IOException;
        public abstract int selectNow( ) throws IOException;
        public abstract void wakeup( );
        public abstract Set keys( );
        public abstract Set selectedKeys( );
}

尽管SelectableChannel类上定义了register( )方法,还是应该将通道注册到选择器上,而不是另一种方式。选择器维护了一个需要监控的通道的集合。一个给定的通道可以被注册到多于一个的选择器上,而且不需要知道它被注册了那个 Selector 对象上。将 register( )放在 SelectableChannel 上而不是 Selector 上,它将返回一个封装了两个对象的关系的选择键对象。重要的是要记住选择器对象控制了被注册到它之上的通道的选择过程。

SelectionKey类的API如下:


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 interestOps (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( )
}
 

选择器才是提供管理功能的对象,而不是可选择通道对象。选择器对象对注 册到它之上的通道执行就绪选择,并管理选择键。

对于键的 interest(感兴趣的操作)集合和 ready(已经准备好的操作)集合的解释是和特定的通道相关的。每个通道的实现,将定义它自己的选择键类。在 register( )方法中构造它并将它传递给所提供的选择器对象。

Show Disqus Comments

Search

    Table of Contents