三种I_O模型知识梳理

这块知识一直感觉挺不好理解的,现有的介绍的文章感觉总结的不是很全,所以自己总结了一版!

一、基本概念

  • BIO(Basic Input Output) :就是传统的I/O操作,同步阻塞IO,当BufferReader 读取输入流中的数据的时候,如果没有读取到有效的数据,程序将在此处阻塞该线程的执行。这里的B有两个意思 一个是Basic 另一个意思为Block 翻译为阻塞。服务器实现模式为一个连接一个线程,即客户端有连接请求的时候,
  • NIO:(New Input Output) :同步非阻塞I/O,也可以翻译为Non-Block Input/Output,新的IO使用了不同的方式来处理输入/输出,新IO采用内存映射文件的方式来处理输入输出,新IO将文件或者文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了。服务器实现模式为一个请求一个线程,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求的时候才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这需要用户进行不停的去询问。
  • AIO:(Asynchronous Input Output):异步非阻塞的IO操作,AIO是在Java7中引入了NIO的改进版NIO2,这是异步IO模型。异步能力最大的一个特点就是异步能力,是一种在读写操作结束之前允许进行其他操作的I/O操作。应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

二、同步与异步,阻塞与非阻塞的理解

  • 阻塞
    阻塞其实就是等待的意思,调用者发起请求如果不能立即拿到结果就一直等,等到结果完成。
  • 非阻塞
    非阻塞就是调用者发起请求后会立即返回,没有等待的过程,但是要自己不断去检查是否已有结果。
    • 同步
      当前线程发起了一个调用或者请求,然后当前线程需要等待该调用结束返回结果才能继续往下进行其他操作。
    • 异步
      当前线程发起了一个调用请求或请求,然后当前线程不需要等待调用的执行结果就可以继续往下执行。
      下面我们将阻塞与异步结合在一起进行理解一下:
      • 同步阻塞:你到饭馆点餐,然后在那里等着,还要一边喊:好了没!饭好了可以吃了。
      • 同步非阻塞:在饭馆点完餐,就去遛狗了,不过遛了一会,就回饭馆喊一声:好了没!
      • 异步阻塞: 在饭馆点完餐,就去遛狗了,遛狗的时候,饭馆给你打电话说饭做好了,让您亲自去拿。
      • 异步非阻塞: 在饭馆点完餐,就去遛狗了,遛狗的时候,饭馆给你打电话说饭做好了,饭馆打电话说,我们知道您的位置,一会给您送过去,安心遛狗就可以了。

什么是I/O?

上面我们对阻塞/非阻塞,异步/同步进行了理解,下面在来了解一下什么是IO

计算机体系结构

冯诺伊曼计算机的体系结构
冯诺依曼的计算机体系结构分为输入设备,计算器,控制器,运算器以及输出设备,这里的IO指的就是 输入输出设备。
为了保证输入输出的安全性以及稳定性,一个进程的地址空间分为用户空间以及内核空间

  • 用户空间 我们平时的应用程序都是运行在用户空间,是用户程序运行的地方。
  • 内核空间 内核空间才能进行系统级别的资源有关的操作,是内核代码的运行空间。
    用户空间与内核空间是相互隔离的,我们想要进行IO操作是要依赖内核空间的能力,但是用户空间的程序是不能直接访问内核空间的,要执行IO操作的时候,由于没有执行权限就要发起系统调用请求来请求操作系统帮忙完成。
    我们常见的IO主要分为磁盘IO(读写文件)网络IO(网络请求)
    在应用程序的角度来看,我们的应用程序其实就是对操作系统的内核发起IO调用,操作系统负责的内核执行具体的IO操作。也就是说,我们的应用程序其实就是发起了Io操作而已,具体执行IO的还是操作系统的内核。

    NIO的基本概念理解

    关于IO的基本概念,我已经在另外一篇文章中进行了总结,这里只对NIO的几个关键词进行总结。
    channel 与buffer是新IO中的两个核心对象。
  • Channel (通道)
    通道是对传统输入输出系统的模拟,在NIO中,所有的数据都需要通过通道进行传输,Channel与传统的InputStream以及OutputStream的主要区别在与它提供了一个map方法,可以通过map方法直接将“一块数据”映射到内存中,这样就实现了传统的输入输出是面向流处理的,但是NIO是面向块处理的。而且通道不同于流的地方还在于通道是双向的,底层的操作系统的通道一般都是全双工的。代表连接到数据源的通道。
  • Buffer(缓冲区)
    Buffer可以理解成一个容器,它的本质是一个数组,发送到channel中的对象都被存放在Buffer中,从channel中读取的数据也必须先放在Buffer中。我们不能直接对channel进行操作,Buffer的主要作用就是用来与NIO进行交互的,数据是从通道中读入缓冲区,从缓冲区写入通道中的。

    channel 与Buffer的关系

    channel 是通道的含义,数据不可以直接在channel上进行处理,必须要把数据读取到缓冲区Buffer中,从通道中读取数据到缓冲区中,从缓冲区写入数据到通道。
    • 多路复用器 Selector 多路复用器
      java NIO 三大基本组件Channel、Buffer、Selector,三大组件的关系
      Selector 提供选择已经就绪的任务的能力。
      Selector 轮询注册在其上的Channel,如果某个Channel发生多谢请求并且Channel就处于就绪状态,会被Selector轮询出来。然后通过SelectionKey可以获取就绪Channel集合,进行后续的I/O操作。一个Selector 可以同时轮询多个Channel,因为JDK使用了epoll()代替传统的select实现,所以就没有最大句柄1024/1024的限制,所以只需要一个线程负责轮询就可以接入成千上万的客户端。
      selector与channel的关系
      channel与buffer的关系

      三种常见的IO的理解

      BIO

      在这里插入图片描述
      同步阻塞模型中,应用程序发起read之后,会一直阻塞,直到内核将数据拷贝到用户空间。

      NIO 模型

      java中的NIO可以理解为I/O多路复用模型。
      在这里插入图片描述
      NIO同步非阻塞,一个线程在同步的进行轮询检查,Selector 不断的轮询注册在其上的Channel,某个Channel 上面连续发生读写连接请求,这个Channel就处于就绪状态,被Selector轮询出来,然后通过SelectionKey就可以获取就绪Channel的集合,进行后续的操作。
      I/O多路复用操纵
      IO多路复用模型中,线程首先发起select调用,询问内核数据是否已经准备就绪,等内核把数据准备好了,用户线程再发起read调用,read调用的过程(数据从内核空间-> 用户空间)还是阻塞的。
      在这里插入图片描述

AIO模型

在这里插入图片描述

是在NIO的基础上引入异步通道的概念,实现异步非阻塞式的IO处理。AIO 不需要通过多路复用器对注册的通道进行轮询操作即可实现异步读写。NIO采用轮询的方式,一直在轮询的询问stream中的数据是否准备就绪,如果准备就绪就发起处理,但是AIO就不需要了,AIO框架在windows下使用windows IOCP技术,在Liunx下使用epoll多路复用技术模拟异步IO,即:应用程序向操作系统注册IO监听,然后继续做自己的事情。操作系统发生IO事件,并且准备好数据后,在主动的通知应用程序,触发相应的函数(这是以一种订阅者模式进行改造)。由于应用程序不是“轮询”方式而是订阅-通知方式,所以不在需要selector 轮询,由channel 通道直接到操作系统注册监听。

应用场景

  • BIO
    适用于连接数目比较少且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,编程比较复杂,程序简单易于理解。
  • NIO
    适用于连接数目比较多的情况下,并且连接比较短(轻操作)的架构。比如聊天服务器。
  • AIO
    适用于连接数目比较多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂

    调用总结

    在这里插入图片描述