NIo、Bio、aio、 的原理及区别与应用场景

本文详细解释了同步、异步、阻塞及非阻塞等IO概念,并对比了同步阻塞IO(BIO)、同步非阻塞IO(NIO)与异步阻塞IO(AIO)的工作原理和技术特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解。具体如下: 

序号问题
1什么是同步?
2什么是异步?
3什么是阻塞?
4什么是非阻塞?
5什么是同步阻塞?
6什么是同步非阻塞?
7什么是异步阻塞?
8什么是异步非阻塞?



散仙不才,在查了一部分资料后,愿试着以通俗易懂的方式解释下这几个名词。如有不足之处,还望告知。 



在弄清楚上面的几个问题之前,我们首先得明白什么是同步,异步,阻塞,非阻塞,只有这几个单个概念理解清楚了,然后在组合理解起来,就相对比较容易了。 

1,同步和异步是针对应用程序和内核的交互而言的。 同步/异步是在时间上强调处理事情的结果/机会成本的两种处理策略;强调结果意味着对结果的迫不急待,不过结果是正确的还是错误的,反正你要立即给我一个结果响应;强调时间机会成本意味着对等待结果浪费的时间极其难接受,而对结果并不是那么急切,暂时不管结果(让处理方处理完主动通知结果/自己空闲的时候主动去获取结果)转而去处理其他事情

2,阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。

 

 

3,同步/异步是宏观上(进程间通讯,通常表现为网络IO的处理上),阻塞/非阻塞是微观上(进程内数据传输,通常表现为对本地IO的处理上);阻塞和非阻塞是同步/异步的表现形式

由上描述基本可以总结一句简短的话,同步和异步是目的,阻塞和非阻塞是实现方式。 
 

编号名词解释举例
1同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪自己上街买衣服,自己亲自干这件事,别的事干不了。
2异步异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知)告诉朋友自己合适衣服的尺寸,大小,颜色,让朋友委托去卖,然后自己可以去干别的事。(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS)
3阻塞所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止去公交站充值,发现这个时候,充值员不在(可能上厕所去了),然后我们就在这里等待,一直等到充值员回来为止。(当然现实社会,可不是这样,但是在计算机里确实如此。)
4非阻塞非阻塞状态下, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待,银行里取款办业务时,领取一张小票,领取完后我们自己可以玩玩手机,或者与别人聊聊天,当轮我们时,银行的喇叭会通知,这时候我们就可以去了。

一:事件分离器

        在IO读写时,把 IO请求 与 读写操作 分离调配进行,需要用到事件分离器。根据处理机制的不同,事件分离器又分为:同步的Reactor和异步的Proactor。

        Reactor模型:

    - 应用程序在事件分离器注册 读就绪事件 和 读就绪事件处理器
    - 事件分离器等待读就绪事件发生
    - 读就绪事件发生,激活事件分离器,分离器调用 读就绪事件处理器(即:可以进行读操作了,开始读)
    - 读事件处理器开始进行读操作,把读到的数据提供给程序使用

        Proactor模型:

       - 应用程序在事件分离器注册 读完成事件 和 读完成事件处理器,并向操作系统发出异步读请求

   - 事件分离器等待操作系统完成读取

   - 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成

   - 事件分离器监听到 读完成事件 后,激活 读完成事件的处理器

   - 读完成事件处理器 处理用户自定义缓冲区中的数据给应用程序使用

    

       同步和异步的区别就在于 读 操作由谁完成:同步的Reactor是指程序发出读请求后,由分离器监听到可以进行读操作时(需要获得读操作条件)通知事件处理器进行读操作,异步的Proactor是指程序发出读请求后,操作系统立刻异步地进行读操作了,读完之后在通知分离器,分离器激活处理器直接取用已读到的数据。

      

        二:同步阻塞IO(BIO)

        我们熟知的Socket编程就是BIO,一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作)。阻塞的原因在于:操作系统允许的线程数量是有限的,多个socket申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝。

 

         三:同步非阻塞IO(NIO)

         New IO是对BIO的改进,基于Reactor模型。我们知道,一个socket连接只有在特点时候才会发生数据传输IO操作,大部分时间这个“数据通道”是空闲的,但还是占用着线程。NIO作出的改进就是“一个请求一个线程”,在连接到服务端的众多socket中,只有需要进行IO操作的才能获取服务端的处理线程进行IO。这样就不会因为线程不够用而限制了socket的接入。客户端的socket连接到服务端时,就会在事件分离器注册一个 IO请求事件 和 IO 事件处理器。在该连接发生IO请求时,IO事件处理器就会启动一个线程来处理这个IO请求,不断尝试获取系统的IO的使用权限,一旦成功(即:可以进行IO),则通知这个socket进行IO数据传输。

         NIO还提供了两个新概念:Buffer和Channel

Buffer:
–        是一块连续的内存块。
–        是 NIO 数据读或写的中转地。
Channel:
–        数据的源头或者数据的目的地
–        用于向 buffer 提供数据或者读取 buffer 数据 ,buffer 对象的唯一接口。
–         异步 I/O 支持
      Buffer作为IO流中数据的缓冲区,而Channel则作为socket的IO流与Buffer的传输通道。客户端socket与服务端socket之间的IO传输不直接把数据交给CPU使用,
而是先经过Channel通道把数据保存到Buffer,然后CPU直接从Buffer区读写数据,一次可以读写更多的内容。
      使用Buffer提高IO效率的原因(这里与IO流里面的BufferedXXStream、BufferedReader、BufferedWriter提高性能的原理一样):IO的耗时主要花在数据传输的路上,普通的IO是一个字节一个字节地传输,
而采用了Buffer的话,通过Buffer封装的方法(比如一次读一行,则以行为单位传输而不是一个字节一次进行传输)就可以实现“一大块字节”的传输。比如:IO就是送快递,普通IO是一个快递跑一趟,采用了Buffer的IO就是一车跑一趟。很明显,buffer效率更高,花在传输路上
的时间大大缩短。

 

          四:异步阻塞IO(AIO)

          NIO是同步的IO,是因为程序需要IO操作时,必须获得了IO权限后亲自进行IO操作才能进行下一步操作。AIO是对NIO的改进(所以AIO又叫NIO.2),它是基于Proactor模型的。每个socket连接在事件分离器注册 IO完成事件 和 IO完成事件处理器。程序需要进行IO时,向分离器发出IO请求并把所用的Buffer区域告知分离器,分离器通知操作系统进行IO操作,操作系统自己不断尝试获取IO权限并进行IO操作(数据保存在Buffer区),操作完成后通知分离器;分离器检测到 IO完成事件,则激活 IO完成事件处理器,处理器会通知程序说“IO已完成”,程序知道后就直接从Buffer区进行数据的读写。

          也就是说:AIO是发出IO请求后,由操作系统自己去获取IO权限并进行IO操作;NIO则是发出IO请求后,由线程不断尝试获取IO权限,获取到后通知应用程序自己进行IO操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值