文章目录
-
- 一、BIO为啥"笨"?NIO为啥"聪明"?
-
- [🌰 生活化类比](#🌰 生活化类比)
- [📊 可视化对比:BIO vs NIO工作流程](#📊 可视化对比:BIO vs NIO工作流程)
-
- [1. BIO工作流程(一对一盯桌)](#1. BIO工作流程(一对一盯桌))
- [2. NIO工作流程(大堂经理统筹)](#2. NIO工作流程(大堂经理统筹))
- 二、NIO三大核心组件:经理、通道、餐盘
-
- [1. Buffer(餐盘):装数据的容器](#1. Buffer(餐盘):装数据的容器)
-
- [🌰 类比](#🌰 类比)
- [📌 核心特点(通俗版)](#📌 核心特点(通俗版))
- [2. Channel(传菜通道):数据的"双向路"](#2. Channel(传菜通道):数据的“双向路”)
-
- [🌰 类比](#🌰 类比)
- [📌 核心特点(通俗版)](#📌 核心特点(通俗版))
- [📋 常见的"传菜通道"](#📋 常见的“传菜通道”)
- [3. Selector(大堂经理):事件"总控"](#3. Selector(大堂经理):事件“总控”)
-
- [🌰 类比](#🌰 类比)
- [📊 Selector工作流程(可视化)](#📊 Selector工作流程(可视化))
- 三、Selector底层模型
如果把Java的I/O比作餐厅服务,BIO是"笨办法"(一对一盯桌),NIO是"聪明办法"(一个经理管所有桌)------核心就是用更少的人(线程)干更多的活(处理更多连接)。接下来用生活化的比喻+可视化图表,把NIO讲得明明白白。
一、BIO为啥"笨"?NIO为啥"聪明"?
🌰 生活化类比
| 场景 | BIO(阻塞I/O) | NIO(非阻塞I/O) |
|---|---|---|
| 餐厅对应角色 | 每个餐桌配1个专属服务员(线程) | 1个大堂经理(Selector)+ 若干机动服务员(少量线程) |
| 服务方式 | 服务员站在餐桌旁,直到客人点餐/结账才走(线程阻塞等待I/O) | 经理在前台盯着所有餐桌,只有客人举手(I/O事件就绪)才派服务员过去(线程只处理有数据的连接) |
| 效率 | 100桌需要100个服务员(线程膨胀,成本高) | 100桌只需要1个经理+2-3个服务员(线程复用,成本低) |
| 适用场景 | 客人少、点完就走(低并发、短连接) | 客人多、边吃边加菜(高并发、长连接) |
📊 可视化对比:BIO vs NIO工作流程
1. BIO工作流程(一对一盯桌)
客人点餐-有数据 客人没点餐-无数据 客人到店-客户端连接 分配专属服务员-创建线程 服务员等客人点餐-线程阻塞等数据 服务员上菜-线程处理I/O 客人离店-连接关闭 服务员空闲-线程销毁
BIO的问题:哪怕客人只是占着桌不点餐,服务员也得一直等,100个桌就需要100个服务员,人多了(线程多了),老板(CPU)要花大量精力协调,效率极低。
2. NIO工作流程(大堂经理统筹)
某桌举手-I/O事件就绪 没人举手-无事件 客人到店-客户端连接 登记到经理本上-注册Channel到Selector 经理盯着所有桌-Selector监听事件 派机动服务员处理-线程处理该连接 处理完回经理处-线程回到Selector
NIO的核心:经理(Selector)只盯"谁有需求",服务员(线程)只处理有需求的桌,没人举手时经理就等着,服务员也不闲着------用极少的人搞定大量桌子。
二、NIO三大核心组件:经理、通道、餐盘
NIO的核心就是3个东西:Selector(大堂经理)、Channel(传菜通道)、Buffer(装菜的餐盘),三者配合才能实现"聪明服务"。
1. Buffer(餐盘):装数据的容器
🌰 类比
Buffer就像餐厅里的餐盘------菜(数据)不能直接拿手递,必须装在餐盘里传;客人点的菜(读数据)先放餐盘,服务员端走;要给客人上菜(写数据),也得先把菜装餐盘里。
📌 核心特点(通俗版)
- 餐盘有固定大小(比如只能装10道菜)→ Buffer有
capacity(容量),创建后不能改; - 装菜时从第一个空位开始装→ Buffer有
position(当前装菜位置),装一个菜挪一格; - 装到指定位置就不能装了→ Buffer有
limit(装菜上限),默认等于容量; - 装完菜要递给客人,得先把餐盘"转个向"→ Buffer的
flip()方法(切换为"取菜模式"),把limit设为当前装菜位置,position归0,避免拿空位置。
2. Channel(传菜通道):数据的"双向路"
🌰 类比
Channel就像餐厅里的传菜通道------连接厨房(数据源)和餐桌(程序),而且是双向的(既能从厨房把菜传到餐桌,也能把餐桌的空盘子传回厨房);而BIO的"流"是单向的(要么只传菜,要么只收盘子)。
📌 核心特点(通俗版)
- 双向通行:既能读数据(拿菜),也能写数据(送菜);
- 必须走餐盘:不能直接拿手递菜,所有数据都要通过Buffer(餐盘)传输;
- 可"喊人":能告诉经理(Selector)"我这有菜要传/要取"(触发I/O事件)。
📋 常见的"传菜通道"
| 通道类型 | 对应场景 | 类比(餐厅) |
|---|---|---|
| FileChannel | 读写本地文件 | 厨房和仓库之间的通道 |
| SocketChannel | 客户端和服务器通信 | 餐厅和客人桌之间的通道 |
| ServerSocketChannel | 服务器监听连接 | 餐厅门口的迎宾通道 |
3. Selector(大堂经理):事件"总控"
🌰 类比
Selector是餐厅的大堂经理------核心工作就是盯着所有传菜通道(Channel),只处理"有动静"的通道:
- 通道注册:所有通道都要在经理这"登记",告诉经理"我要通知你我这有菜要传(读事件)/要装菜(写事件)";
- 监听事件:经理啥也不干,就盯着这些通道,直到有通道喊"我这有菜!"(事件就绪);
- 分派任务:哪个通道有动静,就派服务员(线程)去处理这个通道的事,处理完服务员回来继续待命。
📊 Selector工作流程(可视化)
无通道有动静 有通道喊有菜 创建经理-Selector.open 打开传菜通道-Channel 通道设为非阻塞模式 通道找经理登记-register,说要监听读/写事件 经理继续盯通道 经理拿到有动静的通道列表 派服务员处理该通道的事-读/写数据 处理完,服务员回到经理身边
三、Selector底层模型
JVM给Selector做了一层"统一外壳",底层会调用操作系统提供的"多路复用接口",不同OS用不同的底层模型:
| 操作系统 | Java NIO底层使用的模型 | 补充说明 |
|---|---|---|
| Linux(2.6+) | epoll(默认最优选择) | 高性能核心,NIO高并发的关键依赖 |
| Linux(2.4及前) | poll(降级选择,无epoll支持) | 老系统兼容,性能一般 |
| Windows | IOCP(I/O Completion Port) | 不是select/poll/epoll,是Windows的"多路复用神器",效率和epoll相当 |
| macOS/BSD | kqueue | 类似epoll的高效事件通知模型 |
| 其他嵌入式系统 | poll(若不支持epoll/kqueue) | 兼容优先,性能有限 |
在Linux操作系统底层有三种多路复用模型,分别是select、poll、epoll模型,在Linux2.6以后会自动选择epoll,2.4之前则是poll,以下是三种模型的解读: