核心是保障selectionkey生命周期状态一致、资源不泄漏、访问不越界,需在事件遍历(iterator.remove())、键状态判断(isvalid()后cancel)、附件释放(attach(null))和线程协同(wakeup())四环节严格处理。

处理 SelectionKey 的取消与事件清理,核心不是“调用 cancel()”本身,而是确保整个生命周期中状态一致、资源不泄漏、后续访问不越界。关键动作分布在事件遍历、键状态判断、附件释放和线程协同四个环节。
遍历 selectedKeys 时必须用 Iterator.remove()
Selector.selectedKeys() 返回的是内部可变集合,它不会自动清理已处理的 key。如果跳过 remove,同一个 key 会在下一轮 select 中反复出现,导致重复处理甚至空指针或无限循环。
- 必须用 Iterator
it = selector.selectedKeys().iterator() 遍历 - 每次取出 key 后,立即调用 it.remove() —— 这一步和 cancel() 无关,但必不可少
- 禁止使用增强 for 循环后调用 selectedKeys().remove(key),会抛 UnsupportedOperationException
cancel() 前检查 isValid(),cancel 后不再访问 channel 或 attachment
cancel() 是软注销,不立即释放资源,但会使 key 进入无效状态。继续调用 key.channel() 或 key.attachment() 可能返回 null 或引发异常。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用 cancel() 前先判断 if (key.isValid()),避免对已失效 key 重复操作
- cancel() 后不要访问 key.channel()、key.attachment()、key.interestOps() 等方法
- 如需关闭连接,建议先调 channel.close()(会自动使 key 失效),再 cancel 更安全
主动清理 attachment 并切断强引用
SelectionKey 允许 attach 任意对象(如 ByteBuffer、Handler 实例),cancel() 不会清理这些引用,容易造成内存泄漏。
- 在 cancel() 前取出 attachment:Object att = key.attachment()
- 如有必要,手动释放资源(如 buffer.clear()、handler.destroy())
- 最后调用 key.attach(null),帮助 GC 回收
多线程场景下注意 wakeup() 与注册/取消的可见性
Selector 的 select() 是阻塞操作。若其他线程在 select 阻塞期间调用 register() 或 cancel(),新状态要等下次 select 才生效,可能造成事件遗漏。
- 在并发修改 interestOps 或调用 cancel() 后,可主动调用 selector.wakeup(),唤醒阻塞线程使其立即重新检查状态
- 避免在 select 阻塞时频繁注册/取消;批量操作尽量安排在 select 返回后统一处理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










