Java NIO(New I/O,即 java.nio 包,尤其是基于 Channel/Buffer/Selector 的网络编程)功能强大,但“坑”也非常多。下面按常见程度 + 实战踩坑点给你系统梳理一下。
flip() / clear() / compact() 用错这是最容易出 bug 的地方。
flip() 就读 → 读不到数据clear() / compact() → 下一次写覆盖或读不到buffer.put("hello".getBytes());
// 忘记 flip
channel.read(buffer); // 出错
✅ 建议:
flip()clear() 或 compact()position / limit / capacity 三者容易混淆,出错时没有任何异常,只是“没数据”。
✅ 建议:
buffer.position(), buffer.limit(), buffer.capacity()
ByteBuffer.wrap() 简单场景DirectByteBuffer 内存泄漏ByteBuffer.allocateDirect() 不在 JVM 堆里:
✅ 建议:
System.gc()(不推荐,仅调试)Selector 空轮询(CPU 100%)JDK 曾存在 epoll 空转 bug(Linux):
while (selector.select() == 0) {}
✅ 解决方案:
if (selectedKeys == 0) {
// 计数 + 重开 selector
}
OP_WRITE 滥用导致死循环很多人一写不了就注册 OP_WRITE:
if (!buffer.hasRemaining()) {
key.interestOps(SelectionKey.OP_WRITE);
}
但:
✅ 正确做法:
key.interestOps(SelectionKey.OP_READ);
SelectionKey 忘记取消✅ 建议:
key.cancel();
channel.close();
✅ 建议:
SocketChannel 是非阻塞的,但“看起来像同步”socketChannel.read(buffer);
✅ 必须自己处理:
NIO 不保证消息边界。
❌ 错误:
read() 一次 = 一条消息
✅ 解决:
TCP_NODELAYsocketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
否则:
ByteBuffer 不是线程安全的。
❌ 多线程共享一个 buffer
✅ 每线程一个 / 使用 ThreadLocal
key.interestOps(OP_READ);
doHeavyBusiness(); // 阻塞
✅ 正确:
Too many open files✅ 用 try-with-resources:
try (SocketChannel ch = SocketChannel.open()) {}
IOException: Connection reset-1 和异常✅ 必须:
if (n == -1) close();
| 原生 NIO | Netty |
|---|---|
| 要手写 Selector | 自动 |
| 半包自己解 | codec |
| 空轮询 bug | 已修复 |
| 复杂线程模型 | 封装好 |
✅ 结论:
生产环境:用 Netty
学习原理:写原生 NIO
Java NIO 的坑,90% 都在 Buffer 状态 + Selector 事件 + 半包处理 上。
如果你愿意,我可以:
你想看哪个?
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。