NIO 非阻塞模式
非阻塞 IO 是 NIO 的核心特性,通过 Selector 实现高效的多连接管理。
阻塞 vs 非阻塞
| 特性 | 阻塞 IO | 非阻塞 NIO |
|---|---|---|
| 等待数据 | 等待直到数据就绪 | 立即返回,未就绪返回 0 |
| 线程模型 | 一个连接一个线程 | 单线程管理多连接 |
| 资源消耗 | 线程多,消耗大 | 线程少,消耗小 |
| 适用场景 | 低并发 | 高并发 |
非阻塞模式设置
Java
// Channel 设置为非阻塞
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false); // 关键:设为非阻塞
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 服务端通道非阻塞
必须设置非阻塞:默认是阻塞模式,必须调用 configureBlocking(false)。
Selector 工作流程
1. 创建 Selector
Java
Selector selector = Selector.open();
2. 注册 Channel
Java
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false); // 非阻塞
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
3. 事件循环
Java
while (true) {
// 等待至少一个事件发生
int readyCount = selector.select(); // 阻塞等待
// int readyCount = selector.selectNow(); // 立即返回
if (readyCount == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove(); // 移除已处理的 key
if (key.isAcceptable()) {
handleAccept(key);
}
if (key.isReadable()) {
handleRead(key);
}
if (key.isWritable()) {
handleWrite(key);
}
}
}
4. 处理事件
Java
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept(); // 接收连接
client.configureBlocking(false);
client.register(key.selector(), SelectionKey.OP_READ);
}
private void handleRead(SelectionKey key) throws IOException {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = client.read(buffer); // 非阻塞,未就绪返回 0
if (len == -1) {
client.close(); // 连接关闭
} else if (len > 0) {
buffer.flip();
// 处理数据
}
}
select() 方法区别
| 方法 | 行为 |
|---|---|
| select() | 阻塞直到至少一个事件发生 |
| select(timeout) | 阻塞最多 timeout 毫秒 |
| selectNow() | 立即返回,不阻塞 |
必须移除已处理的 key:iterator.remove() 防止重复处理。
完整服务端示例
Java
public class NioServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
for (SelectionKey key : selector.selectedKeys()) {
selector.selectedKeys().remove(key);
if (key.isAcceptable()) {
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
if (client.read(buffer) == -1) {
client.close();
}
}
}
}
}
}
要点总结
- 非阻塞模式:configureBlocking(false)
- Selector 监控多个 Channel 事件
- select() 阻塞等待事件,selectNow() 立即返回
- 处理完 SelectionKey 必须移除,避免重复处理
- 单线程管理多连接,适合高并发网络场景
- 非阻塞读返回 0 表示数据未就绪,返回 -1 表示连接关闭
📝 发现内容有误?点击此处直接编辑