4.1.1 选择器,可选择通道和选择键类
现在,您也许还对这些用于就绪选择的Java成员感到困惑。让我们来区分这些活动的零件并了解它们是如何交互的吧。图 4-1 的UML图使得情形看起来比真实的情况更为复杂了。看看图 4-2,然后您会发现实际上只有三个有关的类 API,用于执行就绪选择:
FileChannel
对象不是可选择的,因为它们没有继承 SelectableChannel
(见图 4-2)。所有socket通道都是可选择的,包括从管道(Pipe
)对象的中获得的通道。SelectableChannel
可以被注册到Selector
对象上,同时可以指定对那个选择器而言,那种操作是感兴趣的。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。SelectableChannel.register()
返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),指示了该注册关系所关心的通道操作,以及通道已经准备好的操作。让我们看看SelectableChannel
的相关API方法
- public abstract class SelectableChannel extends AbstractChannel implements Channel {
- // 这里仅列出部分API
- 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();
- }
非阻塞特性与多元执行特性的关系是十分密切的——以至于java.nio
的架构将两者的 API放到了一个类中。
我们已经探讨了如何用上面列出的SelecableChannel
的最后三个方法来配置并检查通道的阻塞模式(详细的探讨请参考 3.5.1 小节)。通道在被注册到一个选择器上之前,必须先设置为非阻塞模式(通过调用configureBlocking(false)
)。
调用可选择通道的register()
方法会将它注册到一个选择器上。如果您试图注册一个处于阻塞状态的通道,register()
将抛出未检查的IllegalBlockingModeException
异常。此外,通道一旦被注册,就不能回到阻塞状态。试图这么做的话,将在调用configureBlocking()
方法时将抛出IllegalBlockingModeException
异常。
并且,理所当然地,试图注册一个已经关闭的SelectableChannel
实例的话,也将抛出ClosedChannelException
异常,就像方法原型指示的那样。
在我们进一步了解register()
和SelectableChannel
的其他方法之前,让我们先了解一下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
上,这种做法看起来有点随意。它将返回一个封装了两个对象的关系的选择键对象。重要的是要记住选择器对象控制了被注册到它之上的通道的选择过程。
- 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()
方法中构造它并将它传递给所提供的选择器对象。
在下面的章节里,我们将了解关于这三个类的方法的更多细节。
联系客服