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_READOP_WRITEOP_CONNECTOP_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();
            }
        }
    }
}