文章目录
I . NIO 模型
II . NIO 三大组件交互流程
III . NIO 缓冲区
IV . NIO 与 BIO 对比
V . NIO 线程分配
VI . 缓冲区 ( Buffer ) 示例
I . NIO 模型
NIO 简介 :
① NIO 概念 : NIO 全称为 Non-Blocking IO , 是非阻塞 IO , 与 BIO ( Blocking IO / 阻塞 IO ) 相对应 ;
② NIO 相对于 BIO 的改进 : NIO 在 BIO 的基础上 , 增加了 IO 的性能 ;
③ NIO 模型特点 : NIO 是同步非阻塞模型 , BIO 是同步阻塞模型 ;
④ NIO API 位置 : 在 Java 中 , NIO 定义在 java.nio 包中 ;
⑤ NIO 三大组件 :
通道 Channel : 相当于 BIO 中的 Socket , 用于传输数据 , 向客户端读写数据 ,
缓冲区 Buffer : 每个通道 ( Channel ) 都维护了一个数据缓冲区 ( Buffer ) ; 通道 ( Channel ) 可以读写 缓冲区 ( Buffer ) 中的数据 , 是双向的 ; 客户端 也是读写 缓冲区 ( Buffer ) 中的数据 ; 缓冲区 ( Buffer ) 是 通道 ( Channel ) 与 客户端 之间的缓冲区 ;
选择器 Selector : 选择器 ( Selector ) 根据客户端请求 , 选择指定的 通道 ( Channel ) 为客户端进行服务 ;
II . NIO 三大组件交互流程
NIO 服务器端 交互流程 :
① 启动线程 : 服务器端启动一个线程 ;
② 选择器 ( Selector ) 遍历 通道 ( Channel ) : 线程通过 选择器 ( Selector ) 不同的遍历各个 通道 ( Channel ) , 如果发现有 客户端 对应的 通道 ( Channel ) 有网络请求 , 那么开始处理该 通道 ( Channel ) 相关业务逻辑 ;
③ 通道 ( Channel ) 与 缓冲区 ( Buffer ) 交互 : 通道 ( Channel ) 可以 读写 缓冲区 ( Buffer ) 中的数据 ;
④ 缓冲区 ( Buffer ) 与 客户端交互 : 缓冲区 ( Buffer ) 与 客户端 进行数据读写交互 ;
III . NIO 缓冲区
缓冲区 机制 : 缓冲区 ( Buffer ) 向上与 通道 ( Channel ) 进行数据读写交互 , 向下与 客户端 进行数据读写交互 , 客户端 与 通道 ( Channel ) 不直接进行数据通信 ;
① 缓冲区 ( Buffer ) 作用 : 缓冲区 ( Buffer ) 是实现非阻塞机制的重要途径 ;
② 编程风格 : NIO 也称为 面向 缓冲区 编程 ;
③ BIO 阻塞机制 : BIO 中 客户端 与 服务器端 进行交互 , 需要阻塞等待服务器的响应 , 服务器在建立连接后 , 也需要阻塞等待客户端的后续数据 ;
④ NIO 非阻塞机制 : 客户端请求服务器端后 , 将请求数据写入服务器端的 缓冲区 ( Buffer ) 中 , 服务器端通过 选择器 ( Selector ) 轮询 通道 ( Channel ) , 查询 缓冲区 ( Buffer ) 中是否有请求数据 , 客户端不用阻塞等待服务器端响应 , 服务器端也不用阻塞等待客户端的请求 , 因此这里实现了非阻塞机制 ;
非阻塞说明 : 当选择器 ( Selector ) 选择某个 通道 ( Channel ) 时 , 服务器端线程 从通道 ( Channel ) 中读取用户请求的数据 , 读取完毕之后 , 处理该请求处理 , 如果没有读取到用户请求数据 , 就会轮询其它的 通道 ( Channel ) , 如果所有的 通道 ( Channel ) 都没有事件触发 , 线程做其它事情 , 不会在此阻塞等待用户数据 ;
基于事件驱动 : 选择器 ( Selector ) 可以感知到 通道 ( Channel ) 中的事件 , 线程就会处理与该通道 ( Channel ) 相关业务 , 如果 通道 ( Channel ) 没有触发事件 , 那么线程去做其它事 ;
IV . NIO 与 BIO 对比
1 . 数据处理方式对比 :
① BIO 数据处理方式 : BIO 以 流的方式读写数据 , 输入流 读取数据 , 输出流 写出数据 ; 输入流 和 输出流 又分别有 字节流 , 字符流 分类 ;
② NIO 数据处理方式 : NIO 以 缓冲区 ( Buffer ) 数据块的方式处理数据 , 该处理数据的效率 , 远远高于以 流 的方式读写数据的效率 ;
客户端 与 服务器交互时 , 客户端将数据 写入到 缓冲区 ( Buffer ) , 等待服务器端 通道 ( Channel ) 读取该缓冲区的数据 ;
服务器 与 客户端交互时 , 服务器将数据 通过 通道 ( Channel ) 写出到缓冲区中 , 等待 客户端 读取 ;
2 . IO 模型 阻塞类型对比 : BIO 是 同步阻塞 型 IO ; NIO 是 同步非阻塞 型 IO ;
V . NIO 线程分配
BIO 模型 : 在 BIO 模型中 , 如果 一万 客户端 与 服务器端保持连接通信 , 并进行数据交互 , 就需要有 一万个线程 维护这些操作 ;
BIO 模型中 , 10000 1000010000 客户端连接 , 对应 10000 1000010000 线程 ;
NIO 模型 : 在 NIO 模型中 , 如果 一万 客户端 与 服务器端保持连接通信 , 并进行数据交互 , 那么假设分配 100 100100 个线程 , 每个线程都有对应的 选择器 ( Selector ) , 每个 选择器 ( Selector ) 轮询 100 100100 个 通道 ( Channel ) , 每个 通道 ( Channel ) 对应 一个 缓冲区 ( Buffer ) ;
NIO 模型中 , 10000 1000010000 客户端连接 , 对应 10000 1000010000 个缓冲区 ( Buffer ) , 10000 1000010000 个 通道 ( Channel ) , 100 100100 个线程 ;
VI . 缓冲区 ( Buffer ) 示例
Buffer 有 7 77 个子类 , 分别对应 8 88 大基础数据 ( Boolean 除外 ) , 这里使用 IntBuffer 作示例说明 ;
缓冲区 ( Buffer ) 代码示例 :
需求 : 创建一个 存放 int 数据的 缓冲区 ( Buffer ) , 其容量为 8 88 , 将 8 88 个 int 值存入缓冲区 , 翻转后 , 按照存放顺序打印出来 ;
import java.nio.IntBuffer;
public class BufferDemo { public static void main(String[] args) { //创建一个存储 Int 类型数据的 Buffer , 可以存储 8 个 Int 数据 IntBuffer buffer = IntBuffer.allocate(8); //向 Buffer 中写入数据 for(int i = 0; i < buffer.capacity(); i ++){ buffer.put(i); } //从 Buffer 中取出数据 //先将 Buffer 翻转一下 , 然后读取 , 读出的数据与存储的数据顺序一样 buffer.flip(); //循环读取 buffer 中的 Int 数据, 维护了一个索引 , //代表当前操作的数据索引 , 即 position while (buffer.hasRemaining()){ System.out.println("position " + buffer.position() + " . " + buffer.get()); } } }
执行结果 :
position : 0 . 0 position : 1 . 1 position : 2 . 2 position : 3 . 3 position : 4 . 4 position : 5 . 5 position : 6 . 6 position : 7 . 7