Skip to content

IO 与 NIO 学习总览

IO 是程序和外部世界交换数据的能力。文件读写、网络请求、上传下载、日志落盘、Socket 通信、数据库驱动、Redis 客户端、MQ 客户端,底层都离不开 IO。

零基础先记住一句话:

IO 解决“数据怎么进来、怎么出去”;NIO 解决“高并发连接下,如何少用线程也能处理很多 IO 事件”。

Java 学 IO 不能只看最新 JDK。企业项目和面试里最常见的是:

版本重点
JDK 7try-with-resourcesPathFilesWatchServiceAsynchronousFileChannel,这是现代文件 API 的基础
JDK 8企业主流版本,常见项目大量使用 InputStreamReaderFilesFileChannelSocketChannelSelector
JDK 11+Files.readString/writeString、HTTP Client 等更方便,但不能替代 JDK7/8 基础

本目录按学习顺序拆分:

文档解决的问题学完能做什么
传统 IO字节流、字符流、装饰器、阻塞读写会安全读写文件、理解为什么会阻塞
缓冲与字符编码缓冲为什么快,乱码怎么来能处理 UTF-8 文本、避免大文件 OOM
IO 与 NIO 全过程原理从 Java 代码到操作系统内核的完整链路能讲清 read/write、阻塞、缓冲、Selector、零拷贝和排查过程
NIO Buffer 与 ChannelBuffer 状态机、Channel 双向通道会用 FileChannel 和 ByteBuffer
Selector 多路复用一个线程如何管理多个连接理解 Netty/Reactor 的前置知识
零拷贝与大文件transferTo、mmap、直接内存能解释高性能文件传输和 DirectBuffer OOM
排查与面试高频问题、线上故障、面试回答能把原理讲成项目经验

为什么要学 IO

很多线上问题表面看是接口慢、线程池满、服务假死,根因却是 IO 没理解:

现象常见 IO 根因后果
文件导入很慢小块频繁读磁盘,没有缓冲CPU 不高但耗时长
上传接口卡死阻塞读一直等客户端数据Tomcat 工作线程被占满
日志写不出来磁盘满或句柄泄漏排查时没有证据
中文乱码字节编码和解码字符集不一致数据入库错误
大文件导出 OOM一次性读入内存JVM 堆被打满
Netty 直接内存 OOMDirectBuffer 未释放或超限OutOfMemoryError: Direct buffer memory
NIO 服务 CPU 100%Selector 空轮询或 OP_WRITE 使用错误服务无响应但 CPU 很高

IO 的本质

程序读文件不是 Java 对象直接伸手去磁盘拿数据,而是经历操作系统内核。

mermaid
flowchart TD
    A["Java 代码"] --> B["JVM 本地方法"]
    B --> C["操作系统内核"]
    C --> D["磁盘 / 网卡"]
    D --> C
    C --> E["内核缓冲区"]
    E --> F["用户态缓冲区"]
    F --> A

这张图说明几个关键点:

  1. IO 往往会发生用户态和内核态切换。
  2. 磁盘、网络比内存慢得多,所以 IO 常常是等待外部资源。
  3. 缓冲区能减少系统调用次数和复制次数。
  4. NIO、零拷贝、直接内存,本质都是围绕“减少等待、减少线程、减少复制”做优化。

BIO、NIO、AIO 一句话区分

模型全称核心特点适合场景
BIOBlocking IO同步阻塞,线程发起 IO 后一直等连接少、逻辑简单、文件读写
NIONon-blocking IO同步非阻塞,多路复用,一个线程监听多个 Channel高并发网络连接、网关、长连接
AIOAsynchronous IO异步 IO,操作系统完成后回调部分文件/网络异步场景,Java 后端使用相对少

注意:NIO 的 N 在 Java 语境里常被理解为 New IO,也常被拿来讲 Non-blocking IO。面试时要说清楚:Java NIO 这套 API 包括 Buffer、Channel、Selector,其中网络 Channel 可以配置成非阻塞,并配合 Selector 实现多路复用。

同步、异步、阻塞、非阻塞别混了

这四个词经常被混用。可以从两个维度理解:

维度关心的问题例子
同步/异步结果由谁来通知同步是调用方自己等结果;异步是完成后回调/通知
阻塞/非阻塞等待时线程是否被挂住阻塞读不到数据就停住;非阻塞读不到就立即返回
mermaid
flowchart TD
    A["发起 IO 调用"] --> B{"是否马上返回"}
    B -- "不返回" --> C["阻塞"]
    B -- "马上返回" --> D["非阻塞"]
    A --> E{"完成结果谁来推动"}
    E -- "调用方继续等待或轮询" --> F["同步"]
    E -- "系统完成后通知回调" --> G["异步"]

BIO 是同步阻塞;Java NIO 常见网络用法是同步非阻塞;AIO 是异步。

商业项目怎么用

场景推荐能力原因
小文件配置读取Files.newBufferedReader简单、安全、编码明确
大文件导入BufferedInputStreamFileChannel 分块读取避免一次性读入内存
文件下载分块写响应流,必要时使用 FileChannel.transferTo降低内存占用
医疗数据采集文件落盘临时文件 + 校验 + 原子移动避免半文件被业务读取
高并发长连接Netty,而不是手写复杂 NIO解决粘包、内存池、线程模型、连接管理
日志采集或网关NIO/Reactor/Netty大量连接不能一个连接一个线程

学习建议

  1. 先学传统 IO:字节流、字符流、缓冲、关闭资源。
  2. 再学编码:乱码本质是同一批字节被用错误字符集解释。
  3. 再看 IO 与 NIO 全过程原理:把 read/write、阻塞、缓冲、Selector、零拷贝和排查串起来。
  4. 再学 NIO:先把 Buffer 的 position/limit/capacity 学明白。
  5. 再学 Selector:理解一个线程为什么能管理多个连接。
  6. 最后看零拷贝、直接内存、Netty,把原理和生产问题连起来。

面试标准回答

IO 是 Java 和文件、网络等外部资源交换数据的能力。传统 IO 面向流,通常是同步阻塞模型,代码简单但连接多时线程成本高。NIO 面向 Buffer 和 Channel,网络 Channel 可以配置非阻塞,再通过 Selector 多路复用,让一个线程监听多个连接的读写事件。文件处理常用缓冲流、FilesFileChannel;高并发网络服务一般不会手写 NIO,而是使用 Netty 这类成熟框架。生产中还要注意编码、资源关闭、大文件分块、直接内存和文件句柄泄漏。

关联知识点