SelectionKey API用法

本文详细解析Java NIO Channels中的SelectionKey类,包括其用途、构造方法、方法摘要以及关键字段,帮助开发者理解如何在多线程环境下高效地进行I/O操作。

java.nio.channels
类 SelectionKey

java.lang.Object
  继承者 java.nio.channels.SelectionKey
直接已知子类:
AbstractSelectionKey

public abstract class SelectionKey
    
    
     
     extends Object
    
    
 
 

表示 SelectableChannel 在 Selector 中的注册的标记。

每次向选择器注册通道时就会创建一个选择键。通过调用某个键的 cancel 方法、关闭其通道,或者通过关闭其选择器来取消 该键之前,它一直保持有效。取消某个键不会立即从其选择器中移除它;相反,会将该键添加到选择器的已取消键集,以便在下一次进行选择操作时移除它。可通过调用某个键的 isValid 方法来测试其有效性。

选择键包含两个表示为整数值的操作集。操作集的每一位都表示该键的通道所支持的一类可选择操作。

  • interest 集合 确定了下一次调用某个选择器的选择方法时,将测试哪类操作的准备就绪信息。创建该键时使用给定的值初始化 interest 集合;之后可通过 interestOps(int) 方法对其进行更改。

  • ready 集合 标识了这样一类操作,即某个键的选择器检测到该键的通道已为此类操作准备就绪。创建该键时 ready 集合被初始化为零;可以在之后的选择操作中通过选择器对其进行更新,但不能直接更新它。

选择键的 ready 集合指示,其通道对某个操作类别已准备就绪,该指示只是一个提示,并不保证线程可执行此类别中的操作而不导致被阻塞。ready 集合很可能一完成选择操作就是准确的。ready 集合可能由于外部事件和在相应通道上调用的 I/O 操作而变得不准确。

此类定义了所有已知的操作集位 (operation-set bit),但是给定的通道具体支持哪些位则取决于该通道的类型。SelectableChannel 的每个子类都定义了 validOps() 方法,该方法返回的集合恰好标识该通道支持的操作。试图设置或测试某个键的通道所不支持的操作集位将导致抛出相应的运行时异常。

通常必须将某个特定于应用程序的数据与某个选择键相关联,例如表示高级协议状态的对象和为了实现该协议而处理准备就绪通知的对象。因此,选择键支持将单个任意对象附加 到某个键的操作。可通过 attach 方法附加对象,然后通过 attachment 方法获取该对象。

多个并发线程可安全地使用选择键。一般情况下,读取和写入 interest 集合的操作将与选择器的某些操作保持同步。具体如何执行该同步与实现有关:在一般实现中,如果正在进行某个选择操作,那么读取或写入 interest 集合可能会无限期地阻塞;在高性能的实现中,可能只会暂时阻塞。无论在哪种情况下,选择操作将始终使用该操作开始时当前的 interest 集合值。

从以下版本开始:
1.4
另请参见:
SelectableChannel, Selector

字段摘要
static intOP_ACCEPT           用于套接字接受操作的操作集位。
static intOP_CONNECT           用于套接字连接操作的操作集位。
static intOP_READ           用于读取操作的操作集位。
static intOP_WRITE           用于写入操作的操作集位。

 

构造方法摘要
protected SelectionKey()           构造此类的一个实例。

 

方法摘要
 Objectattach(Object ob)           将给定的对象附加到此键。
 Objectattachment()           获取当前的附加对象。
abstract  voidcancel()           请求取消此键的通道到其选择器的注册。
abstract  SelectableChannelchannel()           返回为之创建此键的通道。
abstract  intinterestOps()           获取此键的 interest 集合。
abstract  SelectionKeyinterestOps(int ops)           将此键的 interest 集合设置为给定值。
 booleanisAcceptable()           测试此键的通道是否已准备好接受新的套接字连接。
 booleanisConnectable()           测试此键的通道是否已完成其套接字连接操作。
 booleanisReadable()           测试此键的通道是否已准备好进行读取。
abstract  booleanisValid()           告知此键是否有效。
 booleanisWritable()           测试此键的通道是否已准备好进行写入。
abstract  intreadyOps()           获取此键的 ready 操作集合。
abstract  Selectorselector()           返回为此选择器创建的键。

 

从类 java.lang.Object 继承的方法
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

 

字段详细信息

OP_READ

public static final int OP_READ
用于读取操作的操作集位。

假定在选择操作开始时,选择键的 interest 集合中已包含 OP_READ。如果选择器检测到相应的通道已为读取准备就绪、已经到达流的末尾、已经被远程关闭而无法进行进一步的读取,或者有一个挂起的错误,那么它会向该键的 ready 集合中添加 OP_READ,并将该键添加到已选择键集中。

另请参见:
常量字段值

OP_WRITE

public static final int OP_WRITE
用于写入操作的操作集位。

假定在选择操作开始时,选择键的 interest 集合中已包含 OP_WRITE。如果选择器检测到相应的通道已为写入准备就绪、已经被远程关闭而无法进行进一步的写入,或者有一个挂起的错误,那么它会向该键的 ready 集合中添加 OP_WRITE,并将该键添加到已选择键集中。

另请参见:
常量字段值

OP_CONNECT

public static final int OP_CONNECT
用于套接字连接操作的操作集位。

假定在选择操作开始时,选择键的 interest 集合中已包含 OP_CONNECT。如果选择器检测到相应的套接字通道已为完成其连接序列而准备就绪,或者有一个挂起的错误,那么它会向该键的 ready 集合中添加 OP_CONNECT,并将该键添加到已选择键集中。

另请参见:
常量字段值

OP_ACCEPT

public static final int OP_ACCEPT
用于套接字接受操作的操作集位。

假定在选择操作开始时,选择键的 interest 集合中已包含 OP_ACCEPT。如果选择器检测到相应的服务器套接字通道已为接受另一个连接而准备就绪,或者有一个挂起的错误,那么它会向该键的 ready 集合中添加 OP_ACCEPT,并将该键添加到已选择键集中。

另请参见:
常量字段值
构造方法详细信息

SelectionKey

protected SelectionKey()
构造此类的一个实例。

方法详细信息

channel

public abstract SelectableChannel channel()
返回为之创建此键的通道。即使已取消该键,此方法仍继续返回通道。

返回:
此键的通道

selector

public abstract Selector selector()
返回为此选择器创建的键。即使已取消该键后,此方法仍将继续返回选择器。

返回:
此键的选择器

isValid

public abstract boolean isValid()
告知此键是否有效。

键在创建时是有效的,并在被取消、其通道已关闭或者其选择器已关闭之前保持有效。

返回:
当且仅当此键有效时才返回 true

cancel

public abstract void cancel()
请求取消此键的通道到其选择器的注册。一旦返回,该键就是无效的,并且将被添加到其选择器的已取消键集中。在进行下一次选择操作时,将从所有选择器的键集中移除该键。

如果已取消了此键,则调用此方法无效。一旦取消某个键,该键会一直保持无效。

可在任意时间调用此方法。此方法与选择器的已取消键集保持同步,因此如果通过涉及同一选择器的取消或选择操作并发调用它,则它可能会暂时受阻塞。


interestOps

public abstract int interestOps()
获取此键的 interest 集合。

可保证返回的集合仅包含对于此键的通道而言有效的操作位。

可在任意时间调用此方法。是否受阻塞,以及阻塞时间长短都是与实现相关的。

返回:
此键的 interest 集合
抛出:
CancelledKeyException - 如果已取消此键

interestOps

public abstract SelectionKey interestOps(int ops)
将此键的 interest 集合设置为给定值。

可在任意时间调用此方法。是否受阻塞,以及阻塞时间长短都是与实现相关的。

参数:
ops - 新的 interest 集合
返回:
此选择键
抛出:
IllegalArgumentException - 如果集合中的某个位与此键的通道所支持的某个操作不对应,也就是说,如果 set & ~(channel().validOps()) != 0
CancelledKeyException - 如果已取消此键

readyOps

public abstract int readyOps()
获取此键的 ready 操作集合。

可保证返回的集合仅包含对于此键的通道而言有效的操作位。

返回:
此键的 ready 操作集合
抛出:
CancelledKeyException - 如果已取消此键

isReadable

public final boolean isReadable()
测试此键的通道是否已准备好进行读取。

调用此方法的形式为 k.isReadable() ,该调用与以下调用的作用完全相同:

 k.readyOps() & OP_READ != 0

如果此键的通道不支持读取操作,则此方法始终返回 false

返回:
当且仅当 readyOps()  &  OP_READ 为非零值时才返回 true
抛出:
CancelledKeyException - 如果已取消此键

isWritable

public final boolean isWritable()
测试此键的通道是否已准备好进行写入。

调用此方法的形式为 k.isWritable() ,该调用与以下调用的作用完全相同:

 k.readyOps() & OP_WRITE != 0

如果此键的通道不支持写入操作,则此方法始终返回 false

返回:
当且仅当 readyOps()  &  OP_WRITE 为非零值时才返回 true
抛出:
CancelledKeyException - 如果已取消此键

isConnectable

public final boolean isConnectable()
测试此键的通道是否已完成其套接字连接操作。

调用此方法的形式为 k.isConnectable() ,该调用与以下调用的作用完全相同:

 k.readyOps() & OP_CONNECT != 0

如果此键的通道不支持套接字连接操作,则此方法始终返回 false

返回:
当且仅当 readyOps()  &  OP_CONNECT 为非零值时才返回 true
抛出:
CancelledKeyException - 如果已取消此键

isAcceptable

public final boolean isAcceptable()
测试此键的通道是否已准备好接受新的套接字连接。

调用此方法的形式为 k.isAcceptable() 形式,该调用与以下调用的作用完全相同:

 k.readyOps() & OP_ACCEPT != 0

如果此键的通道不支持套接字接受操作,则此方法始终返回 false

返回:
当且仅当 readyOps()  &  OP_ACCEPT 为非零值时才返回 true
抛出:
CancelledKeyException - 如果已取消此键

attach

public final Object attach(Object ob)
将给定的对象附加到此键。

之后可通过 attachment 方法获取已附加的对象。一次只能附加一个对象;调用此方法会导致丢弃所有以前的附加对象。通过附加 null 可丢弃当前的附加对象。

参数:
ob - 要附加的对象,可以为 null
返回:
先前已附加的对象(如果有),否则返回 null

attachment

public final Object attachment()
获取当前的附加对象。

返回:
当前已附加到此键的对象,如果没有附加对象,则返回 null
<think> 首先,用户提出了一个问题:“请问ServerSocketChannel和Selector API详细解读”。这是基于我之前提到的Java NIO组件的讨论。 回顾之前的引用: - 引用[1]:解释了Selector类如何注册Channel进行事件监控。 - 引用[2]:描述了ServerSocketChannel的非阻塞能力和设计亮点。 - 引用[3]:提到了ServerSocketChannel的构造方法和配置。 - 引用[4]:概述了Selector、Channel和Buffer三者之间的关系。 - 引用[5]:详细解析了Selector类的定义、核心方法和工作原理。 用户现在请求详细解读ServerSocketChannel和Selector API。所以,我需要基于这些引用,结合我的知识,提供一个结构清晰的解释。 系统级指令要求: 1. 行内数学表达式使用$...$格式,但这个问题是关于Java API的,可能不需要数学表达式。如果需要,比如涉及算法复杂度,我会用。 2. 独立公式使用$$...$$并单独成段,类似地,可能不适用。 3. LaTeX语法正确——确保如果有公式,正确使用。 4. 使用中文回答。 5. 生成相关问题——在回答结束后,添加相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如[^1]。我需要引用提供的引用源。 回答结构: - 应该清晰、逐步解释ServerSocketChannel和Selector。 - 参考引用:使用[^1]到[^5]来标注来源。 - 最后,生成相关问题。 大纲回答: 1. **介绍Java NIO**:简要解释NIO的概念,强调非阻塞I/O。 2. **详细解释ServerSocketChannel**: - 定义和作用:ServerSocketChannel是用于监听新连接的通道。 - 关键特性:非阻塞模式、状态安全、兼容性等(引用[2]和[^2])。 - 核心方法:如open(), configureBlocking(false), accept(), register(), 等。 - 注册到Selector:如何与Selector集成(引用[1]和[^1])。 - 构造和配置:如从引用[3]中提到的NioServerSocketChannelConfig(引用[^3])。 3. **详细解释Selector**: - 定义和作用:Selector管理多个通道的事件监听(引用[4]和[^4])。 - 继承关系:实现Closeable接口(引用[5]和[^5])。 - 核心方法:open(), register(), select(), selectedKeys(), wakeup(), close()。 - open():工厂模式创建实例(引用[5]和[^5])。 - register():注册通道和事件兴趣集。 - select():阻塞等待事件就绪(引用[5]和[^5])。 - selectedKeys():获取就绪事件集合。 - 其他方法如selectNow(), select(long timeout)。 4. **它们之间的关系**:解释如何组合使用,实现高效非阻塞I/O(基于引用[4]和[^4])。 5. **示例代码**:提供一个简单示例展示如何使用ServerSocketChannel和Selector,类似于引用[1]中的代码。 6. **总结**:强调优势,如高并发性能。 7. **生成相关问题**:在最后部分添加相关问题列表。 确保语言为中文。 生成相关问题: 类似系统指令中的示例,问题应该围绕主题扩展。 例如: 1. ServerSocketChannel和Selector在NIO中的最佳实践是什么? 2. 如何优化Selector的性能? 3. ServerSocketChannel和Selector有什么常见问题或陷阱? 4. 与阻塞I/O相比,非阻塞I/O的优势和劣势? 5. Selector在不同操作系统上的实现差异? 现在,写出回答。 </think> ### ServerSocketChannel和Selector API详细解读 在Java NIO(非阻塞I/O)中,ServerSocketChannel和Selector是实现高性能网络编程的核心组件。它们协同工作,允许单线程管理多个连接,显著提升服务器的并发性能。以下基于您提供的引用资料,我将逐步解释它们的API设计、关键方法、工作流程和使用示例。所有解释均参考了站内引用,并在相关段落末尾添加了引用标识。 --- #### 1. **ServerSocketChannel API详解** ServerSocketChannel是java.nio.channels包中的类,专门用于监听新的TCP连接(如服务器端Socket)。它支持非阻塞模式,可与Selector集成实现高效事件驱动。 - **核心特性**: - **非阻塞能力**:通过configureBlocking(false)方法设置非阻塞模式,允许accept()操作立即返回,避免线程阻塞。这使得单线程能处理多个连接请求,提升并发效率[^2]。 - **状态安全**:ServerSocketChannel在操作前会严格检查状态。例如,未绑定端口时调用accept()会抛出NotYetBoundException,确保操作合法性[^2]。 - **兼容性**:通过socket()方法提供与传统ServerSocket的互操作,便于迁移旧代码[^2]。 - **关键方法**: - **open()**:静态方法创建ServerSocketChannel实例。例如:`ServerSocketChannel serverChannel = ServerSocketChannel.open();`。 - **configureBlocking(boolean block)**:设置阻塞模式。false表示非阻塞,true表示阻塞(默认为true)。 - **bind(SocketAddress endpoint)**:绑定服务器地址和端口。例如:`serverChannel.bind(new InetSocketAddress(8080));`。 - **accept()**:接受新连接请求。在非阻塞模式下,如果没有新连接,立即返回null;否则返回SocketChannel实例。 - **register(Selector sel, int ops)**:将通道注册到Selector,指定感兴趣的事件(如SelectionKey.OP_ACCEPT)。返回SelectionKey对象,用于后续事件处理[^1]。 - **内部配置**: ServerSocketChannel在实现中常配合配置类使用,例如NioServerSocketChannelConfig管理TCP参数(如SO_REUSEADDR选项)。构造时通过super传递事件兴趣集(如SelectionKey.OP_ACCEPT)[^3]。示例: ```java public NioServerSocketChannel(ServerSocketChannel channel) { super(null, channel, SelectionKey.OP_ACCEPT); // 继承并设置接受事件 config = new NioServerSocketChannelConfig(this, javaChannel().socket()); // 配置参数 } ``` --- #### 2. **Selector API详解** Selector是管理多个通道(Channel)的事件监听器。它实现java.nio.channels.Selector抽象类,继承Closeable接口,支持资源关闭。Selector的核心作用是检测通道的I/O就绪状态(如可读、可写),允许单线程处理多个I/O操作,实现高并发[^4][^5]。 - **核心特性**: - **事件驱动模型**:Selector监听注册通道的事件(如OP_ACCEPT、OP_READ),当事件就绪时,通过selectedKeys()返回就绪事件集合。 - **跨平台兼容**:底层使用SelectorProvider(如Linux的EPollSelectorProvider、Windows的WindowsSelectorProvider),通过工厂模式隐藏操作系统差异[^5]。 - **高效选择逻辑**:select()方法阻塞等待事件就绪,selectNow()非阻塞立即返回,select(long timeout)支持超时等待。 - **关键方法**: - **open()**:静态工厂方法创建Selector实例。例如:`Selector selector = Selector.open();`。内部调用SelectorProvider.provider().openSelector(),实现依赖倒置原则[^5]。 - **register(SelectableChannel ch, int ops)**:注册通道并指定事件兴趣集(如SelectionKey.OP_ACCEPT)。返回SelectionKey对象,包含通道和事件的绑定关系[^1]。 - **select()**:阻塞线程,直到至少一个注册事件就绪。返回就绪事件数量。 - **selectNow()**:非阻塞版本,立即返回当前就绪事件数量(如果没有就绪事件则返回0)。 - **selectedKeys()**:获取就绪事件SelectionKey集合。需手动迭代处理,并用Iterator.remove()清除已处理项。 - **wakeup()**:唤醒阻塞在select()上的线程。 - **close()**:关闭Selector并释放资源,实现Closeable接口[^5]。 - **SelectionKey的作用**: SelectionKey表示通道在Selector的注册状态,包含事件兴趣集(interestOps)和就绪事件集(readyOps)。常用常量: - `SelectionKey.OP_ACCEPT`:接受连接事件。 - `SelectionKey.OP_READ`:可读事件。 - `SelectionKey.OP_WRITE`:可写事件。 --- #### 3. **ServerSocketChannel和Selector的协同工作流程** 它们共同构建非阻塞I/O的基础:ServerSocketChannel注册到Selector,监听事件;Selector轮询就绪事件,高效分发处理。 1. **初始化阶段**: - 创建Selector和ServerSocketChannel。 - 设置ServerSocketChannel为非阻塞模式并绑定端口。 - 注册ServerSocketChannel到Selector,指定OP_ACCEPT事件。 ```java Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); // 非阻塞模式 serverChannel.bind(new InetSocketAddress(8080)); // 绑定端口 SelectionKey acceptKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册事件 ``` 2. **事件循环阶段**: - 调用selector.select()等待事件。 - 通过selectedKeys()获取就绪事件集。 - 迭代处理事件:如果是OP_ACCEPT,调用accept()获取新SocketChannel;如果是OP_READ/OP_WRITE,进行数据传输。 ```java while (true) { int readyChannels = selector.select(); // 阻塞等待事件 if (readyChannels == 0) continue; Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { SocketChannel clientChannel = serverChannel.accept(); // 接受新连接 clientChannel.configureBlocking(false); clientChannel.register(selector, SelectionKey.OP_READ); // 注册读事件 } if (key.isReadable()) { // 处理读事件... } keyIterator.remove(); // 清除已处理事件 } } ``` - **优势分析**:这种模型避免了传统的线程-per-connection开销,理论并发性能可达数千连接(取决于系统资源)。Selector的select()操作复杂度为$O(1)$(事件驱动),而非阻塞I/O减少了上下文切换[^4][^5]。 --- #### 4. **总结** ServerSocketChannel负责监听新连接,Selector管理事件轮询,二者结合实现了Java NIO的高效非阻塞I/O。ServerSocketChannel的设计注重状态安全和兼容性[^2],而Selector采用工厂模式和事件检测机制支持跨平台高性能[^5]。实际应用中,需注意: - 注册通道时确保非阻塞模式。 - 正确处理selectedKeys()集合以避免事件丢失。 - 使用Buffers进行数据传输,完成通道与缓冲区的协同[^4]。 如需扩展,可参考Java标准库文档或Netty等框架的实现(如Netty的NioEventLoop优化Selector逻辑)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值