面试系列之IO模型

作者: 杯叔书 | 来源:发表于2022-01-18 10:40 被阅读0次

    1.阻塞IO模型

    最传统的IO模型,就是在读和写的过程中发生阻塞现象。
    用户线程发起IO请求之后,内核会去检查数据是否已就绪。
    如果未就绪,内核就会等待数据就绪,用户线程就会挂起,出让CPU。
    当内核数据就绪,内核就会将数据拷贝到用户线程,并唤醒用户线程,解除阻塞状态。
    典型的阻塞 IO 模型的例子为:
    data = socket.read();
    如果数据没有就绪,就会一直阻塞在 read 方法。

    2.非阻塞 IO 模型

    当用户线程发起一个read操作的时候,不会阻塞,而是立马得到一个结果。
    如果结果是一个error,用户线程就知道数据还没准备好,又继续请求read操作。
    一旦数据就绪,用户线程又继续发起read操作,内核就会拷贝数据到用户线程并返回。
    也就数说,非阻塞IO实际上是不断的轮询检查数据是否准备好,而不会出让CPU。
    典型的非阻塞IO例子:

    while(true){
        data = socket.read();
        if(data!= error){
          // 处理数据
         break;
       }
    }
    

    这种方式,不断的轮询检查,cpu占用率就非常高。

    3.多路复用 IO模型

    多路复用IO模型是目前用的比较多的IO模型,比如Redis就是用该模型,java中的NIO也是用该模型。
    多路复用IO模型就是有一个专门的线程负责管理所有的socket状态,这个线程去轮询多个socket的请求,只有socket有真正的读或写请求的时候,才会发起IO操作。
    在多路复用IO模型中,只用一个线程来专门处理客户端连接,所以不必像前两种一样,一旦有客户端连接就新建一个线程,而且只有真正的客户端读写请求才会发起IO操作,这样就大大减少了资源的使用和占用。
    在java nio中是用 selector.select()去查询有没有可可发起IO操作的通道,没有的就阻塞,一旦有一个用户线程有读写请求,就对该线程发起IO操作。
    多路复用IO模型也是通过轮询的方式来检查数据是否准备,不过他只有一个线程而且是在内核中轮询检查的,非阻塞IO是每个用户线程各自轮询检查,显然资源占用率低,效率也会比较高。
    要注意的是,多路复用IO毕竟还是用轮询的方式,一旦处理时间很长或者响应体很大响应时间很长,就会影响排队中的线程的等待时间。

    4.信号驱动IO模型

    当用户线程发起一个io请求的时候,会在socekt上注册一个信号函数,用户线程会继续执行它的流程,当数据准备就绪,内核会发送一个信号给用户线程,用户线程就会执行信号函数中的逻辑来发起IO操作。

    5.异步IO模型

    用户线程发起read操作的时候,立即去执行其他业务。内核收到一个异步读的请求之后,就开始等待数据准备完成,数据准备完成之后,内核会将数据拷贝到用户线程,拷贝完之后在通知用户线程数据已经读完。跟信号驱动不同的是,信号驱动只是收到一个信号,再去用信号函数读。异步IO是内核直接给你,在跟你说好了。

    小结

    阻塞IO:用户线程阻塞挂起
    非阻塞IO:用户线程不阻塞了但是多个用户线程一直占用CPU
    多路复用IO:只有一个后台线程占用cpu但是处理时间久的话,其他用户线程等待时间较长
    信号驱动IO:线程可以立即去处理其他业务,但是还需要通过信号函数来发起IO操作
    异步IO:不需要信号函数发起IO操作了,而是内核直接拷贝到用户线程并通知

    6.java io包

    java io包

    7.java nio包

    java nio包

    8.java nio主要内容

    传统的io是基于字节流或字符流来操作的,而java nio是基于缓冲和通道来操作的。
    java nio主要包括:channel(通道)、buffer(缓冲)、selector(选择器)
    也就是说,数据都是从通道读到缓存区,或者从缓存区写到通道,而选择器监听着多个通道的事件比如:链接打开事件,数据到达事件,所以单个线程可以监听多个通道。
    另外,java io和java nio最大的区别就是,io是面向流的,nio是面向缓冲的


    java nio模型

    Channel:channel和io中的stream差不多是一个级别的,只不过stream是单向的比如:InputStream(读),OutputStream(写),而channel是双向的既可以读也可以写。
    Buffer:buffer实际上就是一个容器,底层是一个连续数组。channel从文件或网络中读到的数据必须先经过buffer。


    buffer
    如上图:客户端发送请求先经过buffer在统一到channel,服务端接受数据也是先经过channel统一读到buffer中在进行处理。
    selector:selector是nio中核心的类,一个selector能够注册多个channel,并且能够检测出多个channel的事件,只有真正有读写事件的时候才会发起io操作。这样依赖只要用一个线程就能管理多个连接,避免线程的上下文切换,减少系统的开销。

    9.nio中的缓存区

    java io是面向字节或字符的,意味着每次读写都是一个字节或多个字节,频繁的刷盘,而且没有缓存到一个地方也没办法前后移动流的数据。nio中的缓存区就是为了解决这些问题,每次读写都将数据暂存到缓存区然后在批量刷盘,而且还能够自由的前后移动读取缓存中的数据,增加了数据处理的效率和灵活性。

    相关文章

      网友评论

        本文标题:面试系列之IO模型

        本文链接:https://www.haomeiwen.com/subject/wblchrtx.html