◇ SelectionKey API
看看 SelectionKey 的相关 API 方法:
package java.nio.channels;
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();
}
一个键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。前两个方法中反映了这种关系:channel() 方法返回与该键相关的 SelectableChannel 对象,而 selector() 则返回相关的 Selector 对象。
键对象表示了一种特定的注册关系。当应该终结这种关系的时候,可以调用 SelectionKey 对象的 cancel() 方法。可以通过调用 isValid() 方法来检查它是否仍然表示一种有效的关系。当键被取消时,它将被放在相关的选择器的已取消的键的集合里。注册不会立即被取消,但键会立即失效。
当通道关闭时,所有相关的键会自动取消(记住,一个通道可以被注册到多个选择器上)。当选择器关闭时,所有被注册到该选择器的通道都将被注销,并且相关的键将立即被无效化(取消)。一旦键被无效化,调用它的与选择相关的方法就将抛出 CancelledKeyException。
◇ instrest 集合和 ready 集合
一个 SelectionKey 对象包含两个以整数形式进行编码的比特掩码:一个用于指示那些通道/选择器组合体所关心的操作(instrest 集合),另一个表示通道准备好要执行的操作(ready 集合)。
当前的 interest 集合可以通过调用键对象的 interestOps() 方法来获取。最初,这应该是通道被注册时传进来的值。这个 interset 集合永远不会被选择器改变,但你可以通过调用 interestOps() 方法并传入一个新的比特掩码参数来改变它。
interest 集合也可以通过将通道注册到选择器上来改变(实际上使用一种迂回的方式调用 interestOps())。当相关的 Selector 上的 select() 操作正在进行时改变键的 interest 集合,不会影响那个正在进行的选择操作。所有更改将会在 select() 的下一个调用中体现出来。
可以通过调用键的 readyOps() 方法来获取相关的通道的已经就绪的操作。ready 集合是 interest 集合的子集,并且表示了 interest 集合中从上次调用 select() 以来已经就绪的那些操作。
就像之前提到过的那样,有四个通道操作可以被用于测试就绪状态。你可以像上面的代码那样,通过测试比特掩码来检查这些状态,但 SelectionKey 类定义了四个便于使用的布尔方法来为你测试这些比特值:isReadable(),isWritable(),isConnectable() 和 isAcceptable()。每一个方法都与使用特定掩码来测试 readyOps( )方法的结果的效果相同:
if (key.isWritable())
等价于:
if ((key.readyOps() & SelectionKey.OP_WRITE) != 0)
这四个方法在任意一个 SelectionKey 对象上都能安全地调用。不能在一个通道上注册一个它不支持的操作,这种操作也永远不会出现在 ready 集合中。调用一个不支持的操作将总是返回 false,因为这种操作在该通道上永远不会准备好。
通过相关的选择键的 readyOps() 方法返回的就绪状态指示只是一个提示,不是保证。底层的通道在任何时候都会不断改变。其他线程可能在通道上执行操作并影响它的就绪状态。同时,操作系统的特点也总是需要考虑的。
◇ SelectionKey 附件
继续来看 SelectionKey 的 attach 和 attachment API:
public abstract class SelectionKey {
// This is a partial API listing
public final Object attach(Object ob)
public final Object attachment();
}
这两个方法允许你在键上放置一个“附件”,并在后面获取它。这是一种允许你将任意对象与键关联的便捷的方法。这个对象可以引用任何对你而言有意义的对象,例如业务对象、会话句柄、其他通道等等。这将允许你遍历与选择器相关的键,使用附加在上面的对象句柄作为引用来获取相关的上下文。
attach() 方法将在键对象中保存所提供的对象的引用。SelectionKey 类除了保存它之外,不会将它用于任何其他用途。任何一个之前保存在键中的附件引用都会被替换。可以使用 null 值来清除附件。可以通过调用 attachment() 方法来获取与键关联的附件句柄。
如果选择键的存续时间很长,但你附加的对象不应该存在那么长时间,请记得在完成后清理附件。否则,你附加的对象将不能被垃圾回收,你将会面临内存泄漏问题。
回忆下 SelectableChannel 的 register() 方法,有个接受一个 Object 参数的重载方法如下:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, myObject);
等价于:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
key.attach (myObject);
◇ SelectionKey 的并发性
关于 SelectionKey 的最后一件需要注意的事情是并发性。总体上说,SelectionKey 对象是线程安全的,但知道修改 interest 集合的操作是通过 Selector 对象进行同步的是很重要的。这可能会导致 interestOps() 方法的调用会阻塞不确定长的一段时间。选择器所使用的锁策略(例如是否在整个选择过程中保持这些锁)是依赖于具体实现的。幸好,这种多元处理能力被特别地设计为可以使用单线程来管理多个通道。被多个线程使用的选择器也只会在系统特别复杂时产生问题。
分享到:
相关推荐
在NIO服务端编程中,通常会使用`Selector`来监听多个通道的事件,当有新的连接、数据可读或写操作完成时,`Selector`会通知程序,从而避免了传统IO模型中必须为每个连接创建一个线程的开销。 标签“nio nio-socket ...
处理完事件后,记得从选择键集(selected keys set)中移除已处理的通道,避免重复处理。 4. **重复步骤2和3**:根据应用需求,可以再次调用`select()`方法,持续监听通道上的事件。 在这个课程“【IT十八掌徐培成...
总结,Java NIO的选择器是实现高并发I/O的关键组件,通过合理使用选择器,开发者可以构建出高效、可扩展的网络应用程序。在实际开发中,理解并熟练掌握选择器的使用,将极大地提升系统的性能和稳定性。
而NIO通过使用选择器(Selector)和通道(Channel)改变了这种模式,允许单个线程管理多个通道,从而实现了更高的并发性能。 在这个多用户聊天项目中,关键组件可能包括以下几个部分: 1. **服务器端**:服务器...
// 获取所有已选择的键 Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); // 检查连接是否完成 if ...
4. **多路复用器(Selector)和选择键(SelectionKey)**:多路复用器是Java NIO的核心组件,它允许程序同时监视多个通道,而选择键表示通道在选择器中的注册状态,包含了可以执行的操作。 5. **管道(Pipe)**:在...
然后,根据返回的选择键(SelectionKey)集合,可以获取就绪的通道并进行相应的处理,如读取客户端的数据,添加echo前缀后再写回给客户端。 总结来说,这个压缩包的内容可能涵盖了如何在Java NIO中实例化和使用通道...
- 使用选择键 - 如何使用选择器 - 异步关闭性 - 选择器的扩展 5. 正则表达式(Regular Expressions) - 正则表达式的基础知识 - Java正则表达式API - 字符串类的正则表达式方法 - Java正则表达式语法 - ...
- **选择键(SelectionKey)**:每个注册到选择器的通道都会生成一个选择键,它包含了通道的状态信息,如是否可读、可写等。 6. **异常处理** - **中断与关闭**:在非阻塞模式下,我们需要处理线程中断以及通道...
3. **SelectionKey(选择键)**:每个注册到Selector的Channel都会生成一个SelectionKey,它表示Channel与Selector之间的关系。SelectionKey包含了关于Channel的状态信息,比如哪些操作是可以无阻塞执行的。...
- 选择键(SelectionKey) - 示例:简易的客户端服务器通信 - 总结 Java IO 是一个庞大的知识体系,很多人学着学着就会学懵了,包括我在内也是如此,所以本文将会从 Java 的 BIO 开始,一步一步深入学习,引出 ...
- 介绍选择键的作用及如何使用。 - 4.3 使用选择器 - 演示了如何将选择器应用于实际场景中。 - 4.4 异步关闭能力 - 讨论了异步关闭选择器的功能。 - 4.5 选择器扩展性 - 探讨了选择器的扩展性问题及其解决...
5. **选择键(SelectionKey)**:每个注册到选择器上的通道都会生成一个选择键,它可以用来检查通道的状态和设置感兴趣的事件。 6. **读写操作**:在选择器轮询到某个通道有读事件后,通过`SocketChannel.read()`...
在这个“JAVA nio的一个简单的例子”中,我们将探讨如何使用Java NIO进行简单的服务器-客户端通信,并计算字符串的哈希值。 在传统的BIO模型中,每个连接都需要一个线程来处理,当并发连接数量增加时,系统会创建...
5. **处理事件**:获取并遍历选择器的已选择键集,处理就绪的通道。 6. **读/写数据**:使用通道与缓冲区交互,读取或写入数据。 7. **关闭资源**:当不再需要时,关闭通道和选择器。 ### NIO的应用场景 1. **高...
5. 当选择器返回一个选择键(SelectionKey)时,检查其对应的事件,然后处理。 6. 根据需要,从缓冲区读取数据或向缓冲区写入数据,然后关闭通道或选择器。 NIO不仅适用于网络编程,如服务器端的高并发处理,还广泛...