跳转到内容

Java NIO与IO比较

来自代码酷

Java NIO与IO比较[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

Java NIO(New I/O)和传统IO(Input/Output)是Java中处理输入输出的两种不同方式。传统IO基于流(Stream)模型,而NIO基于通道(Channel)和缓冲区(Buffer)模型,支持非阻塞操作。本节将详细比较两者的设计理念、性能差异及适用场景。

核心区别[编辑 | 编辑源代码]

  • IO:面向流、阻塞式、同步操作。
  • NIO:面向缓冲区、非阻塞式、支持选择器(Selector)实现多路复用。

graph LR A[Java IO] -->|流模型| B[单向数据传输] A -->|阻塞| C[线程等待] D[Java NIO] -->|缓冲区模型| E[双向数据传输] D -->|非阻塞| F[线程可处理其他任务] D -->|选择器| G[单线程管理多通道]

详细对比[编辑 | 编辑源代码]

1. 数据流与缓冲区[编辑 | 编辑源代码]

  • IO:通过字节流(InputStream/OutputStream)或字符流(Reader/Writer)直接读写数据。
  • NIO:数据需先写入缓冲区(如ByteBuffer),再通过通道(Channel)传输。
// Java IO读取文件示例
try (FileInputStream fis = new FileInputStream("file.txt")) {
    int data;
    while ((data = fis.read()) != -1) {
        System.out.print((char) data);
    }
}

// Java NIO读取文件示例
try (FileChannel channel = FileChannel.open(Paths.get("file.txt"))) {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while (channel.read(buffer) != -1) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear();
    }
}

2. 阻塞与非阻塞[编辑 | 编辑源代码]

  • IO:线程在读写时会阻塞,直到数据就绪。
  • NIO:线程可注册通道到选择器(Selector),实现非阻塞轮询。
// NIO非阻塞示例(服务器端)
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select(); // 阻塞直到至少一个通道就绪
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> iter = keys.iterator();
    while (iter.hasNext()) {
        SelectionKey key = iter.next();
        if (key.isAcceptable()) {
            // 处理新连接
        }
        iter.remove();
    }
}

3. 性能对比[编辑 | 编辑源代码]

  • IO:适合低并发、简单场景。
  • NIO:高并发下资源占用更少(减少线程数),但编程复杂度更高。

IO线程数=O(n)NIO线程数=O(1)

实际应用场景[编辑 | 编辑源代码]

  • IO:文件上传下载、命令行工具。
  • NIO:聊天服务器、实时交易系统(如股票平台)。

案例:文件复制性能测试[编辑 | 编辑源代码]

以下代码对比两种方式复制1GB文件的速度:

// Java IO
try (InputStream is = new FileInputStream("source.bin");
     OutputStream os = new FileOutputStream("dest.bin")) {
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        os.write(buffer, 0, bytesRead);
    }
}

// Java NIO
try (FileChannel src = FileChannel.open(Paths.get("source.bin"));
     FileChannel dest = FileChannel.open(Paths.get("dest.bin"), StandardOpenOption.CREATE)) {
    dest.transferFrom(src, 0, src.size());
}
性能对比(1GB文件)
方式 耗时(ms)
IO 1200
NIO 800

总结[编辑 | 编辑源代码]

  • 选择IO:简单性优先,数据量小。
  • 选择NIO:高并发需求,需非阻塞或零拷贝优化。

pie title 使用场景占比 "IO: 简单任务" : 40 "NIO: 高并发" : 60