SelectionKey API 用法

本文深入探讨Java NIO框架中的核心组件SelectionKey,解析其在Selector与SelectableChannel间的作用,包括注册、取消、操作集及附加数据等功能,阐述如何通过SelectionKey管理I/O操作的准备就绪状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://www.cnblogs.com/Free-Thinker/p/6231743.html

java.nio.channels
类 SelectionKey

java.lang.Object
  

继承者

java.nio.channels.SelectionKey

直接已知子类:

AbstractSelectionKey


public abstract class SelectionKeyextends 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           用于写入操作的操作集位。

 

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

 

方法摘要
 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>好的,我现在需要回答用户关于Java中网络编程API的问题。用户之前已经问过NIO是否基于TCP,现在他想更全面地了解Java的网络编程API。我需要确保回答结构清晰,覆盖主要部分,并且符合用户提供的格式要求。 首先,回忆一下Java网络编程的主要部分。传统上分为BIO(阻塞I/O)和NIO(非阻塞I/O),还有后来的NIO.2。可能需要提到核心类,如Socket、ServerSocket、DatagramSocket等,以及NIO中的Channel、Selector、Buffer等。另外,URL相关类和新的HTTP客户端也应该包括进去。 用户之前的回答结构是分点详细说明,有代码示例和对比表格,这样的结构效果不错。我需要保持类似的格式,但要根据当前问题调整内容。 接下来要确保覆盖以下几个方面: 1. 传统BIO(阻塞式I/O) 2. NIO(非阻塞I/O) 3. NIO.2(AIO,异步I/O) 4. 高层API(如URL、HttpURLConnection,以及新的HttpClient) 5. 其他工具或框架(如Netty) 需要检查每个部分的类和方法,例如BIO中的Socket和ServerSocket,NIO中的Channel和Selector,NIO.2中的AsynchronousSocketChannel等。同时要提供简单的代码示例,但要注意不要过于复杂,保持简洁。 另外,用户可能想知道不同API的适用场景和优缺点,所以对比或总结部分会有帮助。例如,BIO适合连接数少的场景,NIO适合高并发,NIO.2适合异步操作,而HttpClient适合HTTP请求。 需要确保使用正确的术语,例如区分BIO、NIO、AIO,并正确引用Java版本。比如,HttpClient是在Java 11中正式引入的,而NIO.2(AIO)是在Java 7中加入的。 可能还需要提到一些常见的误区,例如NIO是否总是比BIO快,或者是否所有场景都适合NIO,这有助于用户更全面地理解。 最后,检查是否符合之前的格式要求:分点清晰,使用中文,正确使用LaTeX格式的数学表达式(如果有的话),但这个问题可能不需要数学公式。确保代码块使用正确的语法,并且所有技术术语准确。 现在组织内容,分章节介绍每个API部分,包括核心类、代码示例、适用场景和优缺点。同时,最后的对比总结表格可以帮助用户快速比较不同API的特点。</think>以下是Java网络编程API的全面解析: --- ### **1. 传统BIO(阻塞式I/O)** **核心类** - `Socket`:客户端TCP连接 - `ServerSocket`:服务端TCP监听 - `DatagramSocket`:UDP通信 - **特点**:**同步阻塞模型**,每个连接独占一个线程 **代码示例(TCP服务端)** ```java try (ServerSocket server = new ServerSocket(8080)) { while (true) { Socket client = server.accept(); // 阻塞等待连接 new Thread(() -> { try (InputStream in = client.getInputStream(); OutputStream out = client.getOutputStream()) { // 处理读写 } catch (IOException e) { /* ... */ } }).start(); } } ``` **适用场景**:低并发、简单请求(如内部工具) **缺点**:线程数随连接数线性增长,资源消耗大 --- ### **2. NIO(非阻塞I/O,Java 1.4+)** **核心类** - `Channel`:双向数据通道(如`SocketChannel`、`ServerSocketChannel`) - `Selector`:多路复用器,监控多个通道事件 - `Buffer`:数据缓冲区(如`ByteBuffer`) - **特点**:**同步非阻塞模型**,单线程处理多连接 **代码示例(Selector监听)** ```java Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.bind(new InetSocketAddress(8080)); serverChannel.configureBlocking(false); serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册连接事件 while (true) { selector.select(); // 阻塞直到有事件 Set<SelectionKey> keys = selector.selectedKeys(); for (SelectionKey key : keys) { if (key.isAcceptable()) { // 处理新连接 SocketChannel client = serverChannel.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { // 处理读数据 SocketChannel client = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); client.read(buffer); // ... 处理数据 } } keys.clear(); } ``` **适用场景**:高并发、长连接(如聊天服务器、游戏后端) **优点**:资源利用率高,支持数千并发连接 --- ### **3. NIO.2(AIO,异步I/O,Java 7+)** **核心类** - `AsynchronousSocketChannel`:异步TCP客户端 - `AsynchronousServerSocketChannel`:异步TCP服务端 - `CompletionHandler`:异步操作回调接口 - **特点**:**异步非阻塞模型**,内核通知完成状态 **代码示例(异步TCP服务端)** ```java AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(); server.bind(new InetSocketAddress(8080)); server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { @Override public void completed(AsynchronousSocketChannel client, Void attachment) { server.accept(null, this); // 继续监听新连接 ByteBuffer buffer = ByteBuffer.allocate(1024); client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer bytesRead, ByteBuffer buffer) { // 处理读到的数据 } @Override public void failed(Throwable exc, ByteBuffer buffer) { /* ... */ } }); } @Override public void failed(Throwable exc, Void attachment) { /* ... */ } }); ``` **适用场景**:文件I/O密集型或超大规模连接 **缺点**:编程复杂度高,Linux支持有限(底层仍可能使用EPOLL) --- ### **4. 高层网络API** #### **(1) URL/HttpURLConnection(Java 1.0+)** **用途**:HTTP协议通信 ```java URL url = new URL("https://example.com"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); try (BufferedReader reader = new BufferedReader( new InputStreamReader(conn.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } ``` **缺点**:API老旧,不支持HTTP/2,无法异步 #### **(2) HttpClient(Java 11+)** **核心类** - `HttpClient`:HTTP客户端实例 - `HttpRequest`:构建请求 - `HttpResponse`:处理响应 - **特点**:支持HTTP/2、WebSocket,异步操作 **代码示例** ```java HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://example.com")) .build(); // 同步请求 HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); // 异步请求 client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println); ``` --- ### **5. 第三方框架** - **Netty**:基于NIO的高性能网络框架,支持自定义协议 - **OkHttp**:专注于HTTP客户端的轻量级库 - **Grizzly**:GlassFish服务器的NIO框架 --- ### **对比总结** | **API类型** | **模型** | **适用场景** | **特点** | |---------------|----------------|-----------------------------|-----------------------------------| | BIO | 同步阻塞 | 低并发、简单应用 | 编程简单,资源消耗大 | | NIO | 同步非阻塞 | 高并发、长连接 | 复杂度高,性能优异 | | NIO.2 (AIO) | 异步非阻塞 | 文件I/O或超大规模连接 | 依赖操作系统支持,Linux效果有限 | | HttpClient | 同步/异步 | HTTP/HTTPS通信 | 现代API,支持HTTP/2和WebSocket | --- ### **常见误区** - **误区**:NIO一定比BIO快 **纠正**:在连接数较少时,BIO可能更高效(无上下文切换开销) - **误区**:AIO在所有系统上性能都优于NIO **纠正**:Linux对AIO支持不完善,Windows的IOCP更成熟 --- **总结**: Java网络编程API覆盖从底层TCP/UDP到高层HTTP的全场景需求。选择时需根据**并发量**、**协议类型**和**开发复杂度**权衡: - 快速开发小工具 → BIO - 高并发服务器 → NIO或Netty - HTTP客户端 → HttpClient - 超大规模连接 → NIO.2(需结合系统特性)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值