---
URL: "/zh-CN/blog/2026-04/Java-IO.html"
LLMS_URL: "/zh-CN/blog/2026-04/Java-IO.md"
lastUpdated: true
commentabled: true
recommended: true
title: "Java IO三大模型（BIO/NIO/AIO）超详细总结"
description: "Java IO三大模型（BIO/NIO/AIO）超详细总结"
date:

pageClass: "blog-page-class"
cover: "/covers/java.svg"
---

---
lastUpdated: true
commentabled: true
recommended: true
title: Java IO三大模型（BIO/NIO/AIO）超详细总结
description: Java IO三大模型（BIO/NIO/AIO）超详细总结
date: 2026-04-15 10:34:00 
pageClass: blog-page-class
cover: /covers/java.svg
---

相信很多Java开发刚接触IO模型时，都会被「BIO、NIO、AIO」「同步、异步、阻塞、非阻塞」这些概念绕晕，甚至把它们混为一谈。其实IO模型的本质很简单——就是程序和外部设备（文件、网络、控制台等）之间「传输数据的方式」。

今天咱们不聊复杂的源码硬啃，而是用「烧水做饭」这个生活化场景，一步步拆解所有知识点。

## 一、先破后立：搞懂IO的核心底层——4个关键概念（同步/异步、阻塞/非阻塞） ##

在聊Java IO三大模型之前，必须先分清*同步/异步*和*阻塞/非阻塞*这4个核心概念——它们是理解所有IO模型的基础，而且两者完全是不同维度的概念，千万别混为一谈！

咱们就用最贴近生活的「烧水做饭」案例，一次性讲明白，看完再也不混淆～

### 同步 vs 异步：两件事的「执行顺序」（是否能同时进行） ###

同步和异步，描述的是「*两件独立事情的执行关系*」——是串行执行（先做A再做B），还是并行执行（A和B同时做），和“等待”无关，只看顺序。

还是以「烧水」和「做饭」（两件独立的事）为例，一看就懂：

- 同步：先把水烧开，再开始做饭（串行执行）。只有前一件事（烧水）完全做完，后一件事（做饭）才能启动，*两件事不能同时进行*。

- 异步：把水壶放在炉子上烧着，同时就开始做饭（并行执行）。*两件事互不干扰*，不用等前一件事做完，后一件事就能启动，最后各自完成即可。

> 核心记忆点：同步是「*排队做*」，异步是「*同时做*」。

### 阻塞 vs 非阻塞：做一件事时的「自身状态」（遇到突发情况你在干什么） ###

阻塞和非阻塞，描述的是「*做某一件事的过程中，遇到突发事件时，你的状态是什么*」——和“另一件事是什么”无关，只和你自身的行为有关。

还是围绕「烧水做饭」展开，这次聚焦「做饭这一件事」，遇到「烧水」这个突发事件时，你的两种状态：

假设你正在做饭（切菜、洗菜），这是你当前唯一的核心任务；突然发现，做饭需要热水，得去烧一壶水（突发事件）。此时，你有两种选择，对应两种状态：

- 阻塞：把水壶放在炉子上后，什么都不做，就站在炉子旁边盯着水烧开，期间不切菜、不洗菜——你的核心任务（做饭）暂停，全程等待突发事件（烧水）完成，这就是阻塞。

- 非阻塞：把水壶放在炉子上后，不傻等，而是回到厨房继续切菜、洗菜（或者玩会手机），每隔一会儿去看看水开没开——你的核心任务（做饭）没有暂停，遇到突发事件后，你可以做其他事，不用一直等待，这就是非阻塞。

> 核心记忆点：阻塞是「傻等不干活」，非阻塞是「不等先干别的」。

### 四大组合：用烧水做饭案例，吃透所有组合逻辑 ###

理解了同步/异步（执行顺序）、阻塞/非阻塞（自身状态）后，咱们把它们组合起来，还是用「烧水做饭」的案例，逐个拆解——重点区分容易混淆的组合，尤其是“异步阻塞”为什么不存在。

#### 同步阻塞：最“笨”的方式（对应BIO） ###

**组合逻辑**：同步（做饭必须等烧水完成，串行执行）+ 阻塞（烧水时啥也不做，傻等水开）。

**场景还原**：你决定先烧水、再做饭（同步）；把水壶放炉子上后，不切菜、不玩手机，就站在旁边盯着水烧开，等水完全烧开后，才开始洗菜、切菜（阻塞）。全程只有一件事在进行，效率最低。

#### 同步非阻塞：兼顾“等待”和“效率”（对应NIO） ####

**组合逻辑**：同步（做饭必须等烧水完成，串行执行）+ 非阻塞（烧水时不傻等）。

**场景还原**：你还是要等水烧开才能做饭（同步）；但把水壶放炉子上后，你不傻等，而是回到厨房切菜、洗菜（或者玩会手机），每隔1分钟去看看水开没开（主动检查），等水开后，再切换到做饭的核心步骤（非阻塞）。既没有浪费时间，也没有忽略突发事件。

#### 异步非阻塞：最高效的方式（对应AIO） ####

**组合逻辑**：异步（烧水和做饭同时进行，并行执行）+ 非阻塞（烧水时不傻等）。

**场景还原**：你把水壶放炉子上烧着（不用等水开），同时就开始洗菜、切菜、做饭（两件事并行）（异步）；期间你该做饭做饭，不用专门盯着水壶，水开后会发出“鸣笛声”通知你（被动通知），你听到声音后，再去关火、用热水（非阻塞）。全程两不耽误，效率最高。

#### 异步阻塞：逻辑矛盾，不存在的组合 ####

**组合逻辑**：异步（烧水和做饭同时进行）+ 阻塞（啥也不做，傻等水开）。

**场景还原**：你把水壶放炉子上烧着，按理说可以同时做饭（异步），但你却什么都不做，不做饭、不玩手机，就站在旁边盯着水烧开（阻塞）。这本身就很矛盾——既然选择了“同时做两件事”（异步），又非要“傻等其中一件事”（阻塞），最后和“同步阻塞”没区别，完全浪费了异步的优势。

所以，异步阻塞在实际场景中完全不存在，也没有对应的Java IO模型，咱们不用花时间纠结～

## 二、回归主题：Java三大IO模型（BIO/NIO/AIO）实现原理+场景 ##

搞懂了4个核心概念和组合逻辑，接下来咱们回归Java本身——Java中的IO模型，本质就是「同步/异步」和「阻塞/非阻塞」的不同组合，对应三大模型：BIO（同步阻塞）、NIO（同步非阻塞）、AIO（异步非阻塞）。

咱们逐个拆解它们的实现原理、核心特点，结合前面的案例，让你一看就懂，还能分清什么时候用哪个。

### BIO：同步阻塞IO（最基础、最“笨”，新手入门首选） ###

#### 核心定位 ####

BIO全称「Blocking IO」，即同步阻塞IO，是Java最早期的IO模型，对应咱们前面说的「同步阻塞」组合——程序发起IO请求后（类似烧水），必须等待IO操作（读/写数据）完全完成，期间线程会被挂起，什么都做不了。

#### 实现原理 ####

BIO的核心是「流（Stream）」，数据传输是单向的（输入流读数据、输出流写数据），就像“一根单向的水管”，水只能从一端流到另一端。

实现逻辑很简单：

- 程序（线程）发起IO请求（比如读取一个文件、接收客户端连接）；

- IO操作未完成时，线程会被操作系统挂起（阻塞状态），暂停执行，期间不能做任何其他任务；

- 直到IO操作完全完成（数据读完、连接建立），线程才会被唤醒，继续执行后续代码。

#### 核心特点+对应场景 ###

- 优点：编程简单、逻辑直观，新手容易上手，不用考虑复杂的轮询、回调，直接调用API就能实现IO操作。

- 缺点：效率极低、资源浪费严重。一个线程只能处理一个IO请求，高并发场景下（比如1000个客户端同时连接），需要创建1000个线程，线程切换和挂起会消耗大量系统资源，甚至导致程序崩溃。

- 适用场景：低并发、简单的IO操作，比如本地小文件读写、简单的控制台交互，不需要考虑高性能（比如写一个读取本地文本文件的小工具）。

#### 简单代码示例（BIO文件读取） ####

```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BIOExample {
    public static void main(String[] args) {
        // 读取本地文件（同步阻塞）
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            // readLine() 是阻塞方法：没读到数据/文件结束前，线程会一直挂起
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### NIO：同步非阻塞IO（高并发主流，性能提升关键） ###

#### 核心定位 ####

NIO全称「Non-blocking IO」，即同步非阻塞IO，是JDK 1.4引入的IO模型，对应咱们前面说的「同步非阻塞」组合——程序发起IO请求后，不用等待IO操作完成，线程可以去做其他任务，期间主动轮询检查IO操作是否完成。

它解决了BIO高并发下的资源浪费问题，是目前Java高并发网络编程的主流（比如Netty框架，就是基于NIO封装的）。

#### 实现原理 ####

NIO的核心不再是“流”，而是「通道（Channel）+ 缓冲区（Buffer）+ 选择器（Selector）」，三者协同工作，实现“单线程处理多个IO请求”，也就是咱们常说的「IO多路复用」。

拆解三个核心组件，通俗易懂：

- 通道（Channel）：双向的“水管”，替代BIO的单向流，数据可以双向传输（既能读又能写），比如文件通道（FileChannel）、网络通道（SocketChannel）。

- 缓冲区（Buffer）：数据的“容器”，所有IO操作都通过Buffer完成（读数据：从通道读到缓冲区；写数据：从缓冲区写到通道），避免频繁操作底层资源，提升效率。

- 选择器（Selector）：NIO的核心，相当于“一个调度员”。单线程可以通过Selector，同时管理多个通道，监听通道上的IO事件（比如“有数据可读”“有客户端连接”）；线程不用阻塞等待每个通道的IO完成，而是由Selector通知线程“哪个通道的IO准备好了”，线程再去处理对应的通道。

**实现逻辑总结**：

- 创建通道，并设置为非阻塞模式；

- 将所有通道注册到Selector上，指定要监听的IO事件；

- 线程通过Selector轮询（非阻塞），检查是否有通道的IO事件就绪；

- 如果有就绪的通道，线程就去处理对应的IO操作（读/写数据）；如果没有，线程可以去做其他任务，过会儿再轮询。

#### 核心特点+对应场景 ####

- 优点：高并发性能优秀，单线程可处理多个IO请求，减少线程创建和切换的开销；资源利用率高，线程不用傻等，可并行处理其他任务。

- 缺点：编程复杂度比BIO高，需要理解Channel、Buffer、Selector的协同工作机制；轮询操作会消耗一定的CPU资源（但比BIO的线程阻塞好得多）。

- 适用场景：高并发网络编程，比如服务器、网关、聊天服务器等，需要处理大量连接，但每个连接的数据量不大的场景。

#### 简单代码示例（NIO Socket 服务端，多路复用） ####

```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOExample {
    public static void main(String[] args) throws IOException {
        // 1. 创建 ServerSocketChannel 并设置为非阻塞
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(8888));
        serverChannel.configureBlocking(false); // 关键：设置非阻塞

        // 2. 创建 Selector（选择器）
        Selector selector = Selector.open();
        // 3. 将 ServerSocketChannel 注册到 Selector，监听“连接事件”
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("NIO 服务端启动，监听端口：8888");

        while (true) {
            // 4. 轮询就绪的 Channel（阻塞，直到有事件发生）
            selector.select();
            // 5. 获取所有就绪的 SelectionKey
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();

            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove(); // 必须移除，避免重复处理

                // 6. 处理不同事件
                if (key.isAcceptable()) {
                    // 处理连接事件
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    SocketChannel sc = ssc.accept(); // 非阻塞，不会挂起
                    sc.configureBlocking(false);
                    // 注册读事件
                    sc.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    System.out.println("客户端连接成功：" + sc.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 处理读事件
                    SocketChannel sc = (SocketChannel) key.channel();
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    int read = sc.read(buffer); // 非阻塞，返回读取的字节数
                    if (read > 0) {
                        buffer.flip();
                        String msg = new String(buffer.array(), 0, buffer.limit());
                        System.out.println("收到客户端消息：" + msg);
                        buffer.clear();
                    } else if (read < 0) {
                        // 客户端断开连接
                        sc.close();
                        key.cancel();
                        System.out.println("客户端断开连接：" + sc.getRemoteAddress());
                    }
                }
            }
        }
    }
}
```

## AIO：异步非阻塞IO（高性能天花板，场景受限） ##

### 核心定位 ###

AIO全称「Asynchronous IO」，即异步非阻塞IO，是JDK 1.7引入的IO模型（也叫NIO.2），对应咱们前面说的「异步非阻塞」组合——程序发起IO请求后，直接返回，不用等待、不用轮询，IO操作由操作系统异步完成；当IO操作完成后，操作系统会通过「回调函数」或「Future」通知程序，程序再去处理结果。

它是三者中效率最高的，但受操作系统支持限制，实际应用不如NIO广泛。

### 实现原理 ###

AIO的核心是「异步回调」，程序完全不用关心IO操作的过程，只需要发起请求、注册回调，剩下的都交给操作系统处理，相当于“甩手掌柜”。

实现逻辑很简单，分为3步：

- 程序（线程）发起IO请求（比如读取大文件、接收客户端连接），并注册一个「回调函数」；

- 请求发起后，线程立即返回，继续执行其他任务（非阻塞），IO操作由操作系统在后台异步完成；

- 当IO操作完成（数据读完、连接建立），操作系统会自动调用注册的回调函数，通知程序“IO操作完成”，程序再在回调函数中处理返回的数据。

> 关键区别：NIO是「程序主动轮询」检查IO是否完成，而AIO是「操作系统被动通知」，程序不用做任何等待和轮询，资源利用率达到最高。

### 核心特点+对应场景 ###

- 优点：性能最优，全程非阻塞，线程不用等待、不用轮询，完全解放线程；适合处理大数据量、高延迟的IO操作。

- 缺点：编程复杂度最高，需要理解异步回调、Future等机制；受操作系统支持限制（Windows系统对AIO支持较好，Linux系统对AIO支持有限，实际开发中常用NIO替代）。

- 适用场景：高并发、大数据量、低延迟的IO场景，比如大文件传输、视频流处理、高性能服务器等，且操作系统支持异步IO的场景。

### 简单代码示例（AIO异步回调） ###

```java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AIOExample {
    public static void main(String[] args) throws IOException {
        // 1. 创建异步服务端通道
        AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(8888));
        System.out.println("AIO服务端启动，监听端口8888...");

        // 2. 异步接受连接（注册回调函数，IO完成后自动触发）
        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel sc, Void attachment) {
                // 继续接受下一个客户端连接（否则只能处理一个客户端）
                serverChannel.accept(null, this);
                System.out.println("客户端连接成功：" + sc);

                // 3. 异步读取客户端数据（注册回调）
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                sc.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer readBytes, ByteBuffer buf) {
                        // IO操作完成（读取到数据），处理数据
                        if (readBytes > 0) {
                            buf.flip();
                            String msg = new String(buf.array(), 0, readBytes);
                            System.out.println("收到客户端消息：" + msg);
                            // 继续读取下一次数据
                            sc.read(buf, buf, this);
                        }
                    }

                    @Override
                    public void failed(Throwable exc, ByteBuffer buf) {
                        // IO操作失败，处理异常
                        exc.printStackTrace();
                    }
                });
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                // 连接失败，处理异常
                exc.printStackTrace();
            }
        });

        // 防止主线程退出（主线程不用处理IO，只负责发起请求）
        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
```

## 三、总结：三大IO模型核心对比（一眼分清，快速选型） ##

学到这里，相信你已经吃透了Java IO三大模型和核心概念。最后咱们用一张表格，汇总三者的核心区别，帮你快速选型，避免踩坑：

|  IO模型   |   类型  |   核心优势  | 核心劣势  |   适用场景  |
| :-----------: | :-----------: | :-----------: | :-----------: | :-----------: |
| BIO |  同步阻塞  |   编程简单、逻辑直观  |  效率低、资源浪费，不支持高并发  |   低并发、小文件读写、简单交互  |
| NIO |  同步非阻塞  |   高并发性能优秀，资源利用率高  |  编程复杂度中等，有轮询开销  |   高并发网络编程（服务器、网关等）  |
| AIO |  异步非阻塞  |   性能最优，完全解放线程  |  编程复杂，受操作系统支持限制  |   大数据量、低延迟场景（大文件传输等）  |

