Java NIO(New I/O,主要包括 java.nio 包及其子包,以及 Selector、Channel、Buffer、Charset 等)虽然性能高、可扩展好,但 API 相对底层,很容易踩坑。下面按常见错误类型整理,并给出原因和正确做法。
flip()buffer.put("hello".getBytes());
channel.write(buffer); // 可能写不出去
原因:put 后 position 在末尾,write 从 position 读,什么都读不到。
✅ 正确:
buffer.put(data);
buffer.flip();
channel.write(buffer);
clear() / compact()读取一次后再次使用 Buffer 时数据混乱。
clear():清空,准备写入compact():保留未读数据,准备继续写✅ 读后写:
buffer.compact();
remaining()while (buffer.hasRemaining()) {
// 容易死循环,如果没 flip 或没读干净
}
确保:
flip() 后再读int n = channel.read(buffer); // n 可能为 0
在非阻塞模式下:
n == 0 是正常的n == -1 表示连接关闭✅ 正确循环读取:
while (channel.read(buffer) > 0) {
...
}
SocketChannel channel = SocketChannel.open();
// 忘记 close()
✅ 使用 try-with-resources:
try (SocketChannel ch = SocketChannel.open()) {
...
}
selectionKey.interestOps() 更新写完数据后不取消 OP_WRITE,导致 CPU 空转。
✅ 写完关闭写事件:
key.interestOps(SelectionKey.OP_READ);
select() 后不处理 selectedKeysselector.select();
// 没遍历 selectedKeys
✅ 必须遍历并移除:
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
it.remove();
}
select() 阻塞select() 可能返回 0(超时)if (key.isReadable()) {
Thread.sleep(1000); // 错误
}
✅ 把业务处理丢到线程池。
String.getBytes()buffer.put("中文".getBytes()); // 平台依赖
✅ 明确编码:
buffer.put("中文".getBytes(StandardCharsets.UTF_8));
CharsetDecoder/Encoder跨平台通信建议显式编解码。
ByteBuffer 容量固定不能直接扩容,需要手动创建新 Buffer 并拷贝。
allocate 而非 allocateDirect 的误判DirectBuffer:少一次拷贝,但创建成本高NIO 是流式协议,必须自己定义:
| 类别 | 常见错误 |
|---|---|
| Buffer | 忘 flip / clear / compact |
| Channel | 非阻塞误用、不关闭 |
| Selector | 忘更新 interestOps、忘遍历 keys |
| 线程 | Selector 线程做重活 |
| 编码 | 默认编码、乱码 |
| 协议 | 不处理拆包粘包 |
如果你愿意,我可以:
你可以直接贴代码。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。