NIO(Non-blocking I/O,非阻塞I/O)是Java中用于高效处理I/O操作的API。与传统的BIO(Blocking I/O,阻塞I/O)相比,NIO提供了非阻塞、事件驱动和基于通道的I/O操作,能够更好地支持高并发和高性能的应用场景。
NIO的核心组件
通道(Channel)
通道是数据的传输管道,支持双向读写操作。常见的通道类型如下:
FileChannel
:用于文件I/O;SocketChannel
:用于TCP网络通信;ServerSocketChannel
:用于监听TCP连接;DatagramChannel
:用于UDP网络通信。
缓冲区(Buffer)
缓冲区是数据的临时存储区域,用于在通道和程序之间传输数据。常见的缓冲区类型如下:
ByteBuffer
:存储字节数据;CharBuffer
:存储字符数据;IntBuffer
:存储整数数据。
选择器(Selector)
选择器用于监听多个通道的事件(如连接、读、写等),实现单线程管理多个通道。通过选择器,可以实现非阻塞I/O和事件驱动模型。
NIO的工作原理
NIO的核心思想是通过通道、缓冲区和选择器实现非阻塞I/O操作。
非阻塞模式
在NIO中,通道可以设置为非阻塞模式。在这种模式下,I/O操作不会阻塞线程,即使没有数据可读或可写,线程也可以继续执行其他任务。通过如下方式设置为非阻塞模式:
SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); // 设置为非阻塞模式
缓冲区读写
数据通过缓冲区在通道和程序之间传输。
ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配缓冲区 int bytesRead = channel.read(buffer); // 从通道读取数据到缓冲区 buffer.flip(); // 切换为读模式 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); // 读取缓冲区数据 } buffer.clear(); // 清空缓冲区
选择器与事件驱动
选择器通过监听通道的事件(如
OP_READ
、OP_WRITE
、OP_CONNECT
、OP_ACCEPT
),实现事件驱动的I/O操作。Selector selector = Selector.open(); // 创建选择器 channel.register(selector, SelectionKey.OP_READ); // 注册通道到选择器 while (true) { int readyChannels = selector.select(); // 等待事件 if (readyChannels == 0) continue; Set<SelectionKey> selectedKeys = selector.selectedKeys(); // 获取事件集合 Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isReadable()) { // 处理读事件 } else if (key.isWritable()) { // 处理写事件 } keyIterator.remove(); // 移除已处理的事件 } }
NIO的优点
非阻塞I/O:线程不会被I/O操作阻塞,适合高并发场景;
事件驱动:通过选择器监听多个通道的事件,减少线程开销;
高性能:通过缓冲区和零拷贝技术,提高数据传输效率。
NIO的缺点
编程复杂:NIO的API比BIO复杂,需要处理缓冲区、选择器等细节;
调试困难:非阻塞和事件驱动模型增加了调试的难度;
不适合简单场景:对于简单的I/O操作,NIO可能不如BIO直观和高效。
NIO的应用场景
高并发服务器:如Web服务器、聊天服务器;
文件传输:如大文件的高效读写;
实时通信:如音视频流媒体。
示例代码
以下是一个简单的NIO服务器示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
public static void main(String[] args) throws IOException {
// 创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 创建Selector
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // 等待事件
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// 处理连接事件
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// 处理读事件
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("Received: " + new String(data));
}
}
keyIterator.remove();
}
}
}
}