Java中的BIO、NIO、AIO
源地址:https://blog.csdn.net/zhangzeyuaaa/article/details/50520458
名词解释
在弄清楚什么是BIO
、NIO
和AIO
之前,需要先搞清楚什么是同步
、异步
、阻塞
、非阻塞
- 同步和异步:
是针对应用程序和内核的交互而言的。 - 阻塞和非阻塞:
是针对进程在访问数据的时候,根据IO操作的就绪状态来采取不同的处理方式。
阻塞:读取或者写入时会一直等待,直到读取或者写入完成才能返回。
非阻塞: 会立即返回一个状态值。
举例说明:
名词 | 解释 | 举例 |
---|---|---|
同步 | 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 | 亲自去买奶茶 |
异步 | 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知) | 网上点一杯奶茶,自己该干嘛干嘛,外卖员送过来打电话通知你 |
阻塞 | 所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待状态, 直到有东西可读或者可写为止 | 去食堂打饭,轮到你的时候没饭了,你就在那等,, |
非阻塞 | 非阻塞状态下, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待 | 去食堂吃饭,轮到你没饭了,艹,不吃了 |
- 同步:需要自己去询问事情的结果
- 异步:坐等别人通知你事情的结果
- 阻塞:事情需要自己处理,必要得有个结果,没结果就在那死等
- 非阻塞:事情交给别人处理,自己去潇洒,别人处理完了再告诉你一声
理解BIO、NIO、AIO
- 同步阻塞IO(JAVA BIO):
同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 - 同步非阻塞IO(Java NIO) :
同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。 - 异步阻塞IO(Java NIO):
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序。这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成。
那么为什么说是阻塞的呢?因为此时是通过select
系统调用来完成的,而select
函数本身的实现方式是阻塞的,而采用select
函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select
属于同步操作。因为select
之后,进程还需要读写数据),从而提高系统的并发性! - 异步非阻塞IO(Java AIO(NIO.2)):
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
BIO、NIO、AIO适用场景分析:
BIO
方式适用于连接数目比较少且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。NIO
方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。AIO
方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
Reactor
模式和Proactor
模式。
Reactor
模式
应用于同步I/O的场景。
读取操作:
- 应用程序注册读就绪事件和相关联的事件处理器
- 事件分离器等待事件的发生
- 当发生读就绪事件的时候,事件分离器调用第一步注册的事件处理器
- 事件处理器首先执行实际的读取操作,然后根据读取到的内容进行进一步的处理
写入操作:
类似于读取操作,只不过第一步注册的是写就绪事件。
Proactor
模式
读取操作:
- 应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。
- 事件分离器等待读取操作完成事件
- 在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作(异步IO都是操作系统负责将数据读写到应用传递进来的缓冲区供应用程序操作,操作系统扮演了重要角色),并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序需要传递缓存区。
- 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。
写入操作:
和读取操作类似,只不过感兴趣的事件是写入完成事件。
总结
Reactor
和Proactor
模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备.- 综上所述,同步和异步是相对于应用和内核的交互方式而言的,同步 需要主动去询问,而异步的时候内核在IO事件发生的时候通知应用程序,而阻塞和非阻塞仅仅是系统在调用系统调用的时候函数的实现方式而已。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律