必须用迭代器的remove()方法移除已处理的SelectionKey,因为selectedKeys()返回的Set不支持直接remove或clear,否则会抛异常或破坏Selector状态;正确做法是在遍历中调用it.remove()。

在使用 NIO 的 Selector 时,selectedKeys() 返回的是一个可变的 Set<selectionkey></selectionkey>,它内部由 Selector 自己维护。你**不能也不应该**手动“移除变量键”——关键在于:每次调用 select() 后,已就绪的键会被加入该集合;而**必须在处理完每个键后显式调用 Iterator.remove()**,否则该键会一直留在集合中,导致重复处理或资源泄漏。
为什么必须用迭代器的 remove() 方法
selectedKeys() 返回的 Set 是 Selector 内部持有的特殊集合(通常是 SelectedSelectionKeySet),不支持直接调用 set.remove(key) 或 set.clear()。强行调用可能抛出 UnsupportedOperationException,或破坏 Selector 内部状态,造成后续 select() 行为异常。
正确做法是遍历并安全移除:
- 使用
Iterator<selectionkey> it = selector.selectedKeys().iterator();</selectionkey> - 在循环中对每个
key处理完毕后,立即调用it.remove() - 避免在遍历过程中用集合自身方法删除(如
selectedKeys().remove(key))
典型处理循环结构
标准写法如下(带异常防护):
while (selector.select() > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove(); // ✅ 必须在此处移除
try {
if (key.isValid()) {
if (key.isAcceptable()) handleAccept(key);
if (key.isConnectable()) handleConnect(key);
if (key.isReadable()) handleRead(key);
if (key.isWritable()) handleWrite(key);
}
} catch (IOException e) {
key.cancel(); // 出错时取消键
}
}
}
常见误区与注意事项
-
不要复用 key 集合引用:每次循环都应重新获取
iterator(),不要缓存旧的 Iterator - 不要在处理中途跳过 remove():即使跳过某个分支(如只读不写),只要 key 已被取出,就必须 remove,否则下次 select() 仍会返回它
-
cancel() ≠ remove():调用
key.cancel()是让键失效,但不会从 selectedKeys 中移除当前轮次的引用,仍需 iterator.remove() - 线程安全:Selector 和其 selectedKeys 集合不是线程安全的,所有操作应在同一线程(通常是 IO 线程)中完成
补充:selectedKeys 是“一次性消费”的
Selector 不会自动清空 selectedKeys 集合。它的设计语义就是“你取走、你负责清理”。每次 select() 返回后,新就绪的键被追加进来,而未被 remove 的旧键仍保留在集合中——这正是为什么漏掉 it.remove() 会导致无限重复处理同一个 key,甚至引发 CPU 100%。

















