一句话说透Java里面的NIO

177 阅读2分钟

一句话总结:
NIO 像多线程快递站,一个快递员(线程)能同时管多个快递点(连接),不用干等着收件!


一、NIO 是啥?和传统 IO 对比

  • 传统 IO(BIO)

    • 同步阻塞:每个连接配一个线程,线程在 read() 时干等(像单线程快递员,一次只能送一个包裹)。
    • 问题:连接一多,线程爆炸,性能暴跌!
  • NIO(New IO)

    • 同步非阻塞:一个线程处理多个连接,没数据就立马干别的,不用傻等(像快递员同时盯多个快递柜,有货就取)。
    • 核心三件套Channel(通道)、Buffer(缓冲区)、Selector(多路复用器)。

二、NIO 核心三件套

1. Channel(通道)—— 传输管道

  • 双向传输:既能读,也能写(传统 IO 流是单向的)。

  • 类型

    • FileChannel:文件传输
    • SocketChannel:TCP 网络通信
    • DatagramChannel:UDP 网络通信
// 从文件读数据到 Buffer  
try (FileChannel channel = FileChannel.open(Path.of("test.txt"))) {  
    ByteBuffer buffer = ByteBuffer.allocate(1024);  
    channel.read(buffer);  
}  

2. Buffer(缓冲区)—— 临时仓库

  • 本质:一块内存(数组),数据先读到 Buffer,再处理。

  • 关键属性

    • capacity:容量(最大能装多少)
    • position:当前位置(读到哪/写到哪)
    • limit:可操作数据上限
  • 操作口诀

    • 写数据buffer.put() → flip()(切换读模式)
    • 读数据buffer.get() → clear() 或 compact()(清空复用)
ByteBuffer buffer = ByteBuffer.allocate(1024);  

// 写数据  
buffer.put("Hello".getBytes());  
buffer.flip(); // 切换读模式  

// 读数据  
while (buffer.hasRemaining()) {  
    System.out.print((char) buffer.get());  
}  
buffer.clear(); // 清空复用  

3. Selector(选择器)—— 多路复用器

  • 作用:一个线程监听多个 Channel 的事件(连接、读、写)。

  • 核心代码

    Selector selector = Selector.open();  
    SocketChannel channel = SocketChannel.open();  
    channel.configureBlocking(false); // 非阻塞模式  
    channel.register(selector, SelectionKey.OP_READ); // 注册读事件  
    
    while (true) {  
        int readyChannels = selector.select(); // 阻塞直到有事件  
        if (readyChannels == 0) continue;  
    
        Set<SelectionKey> keys = selector.selectedKeys();  
        for (SelectionKey key : keys) {  
            if (key.isReadable()) {  
                // 处理读事件  
                SocketChannel ch = (SocketChannel) key.channel();  
                ByteBuffer buf = ByteBuffer.allocate(1024);  
                ch.read(buf);  
            }  
            keys.remove(key);  
        }  
    }  
    

三、NIO 为啥快?三大优势

  1. 非阻塞:线程不用等数据,没活干就去处理其他 Channel。
  2. 零拷贝:通过 FileChannel.transferTo() 直接传输文件,减少内存复制(快递直达,不用中转)。
  3. 多路复用:一个线程管理多个连接,节省线程资源。

四、适用场景

  • 高并发服务器:如 Web 服务器(Tomcat、Netty)、即时通讯(IM)。
  • 大文件传输:零拷贝加速文件传输。
  • 需要精细控制 IO 的场景:如自定义协议解析。

五、注意事项(坑!)

  1. API 复杂:Buffer 的状态切换(flip/clear)容易出错。
  2. 空轮询 Bug:某些 Linux 版本下 selector.select() 会立即返回,导致 CPU 100%(Netty 已修复)。
  3. 代码难写:相比传统 IO,NIO 需要自己处理事件循环、状态管理。

总结口诀:

“传统 IO 线程多,阻塞等待效率低。
NIO 三件套,Channel、Buffer、Selector。
非阻塞加多路复用,高并发场景真给力!”