Java 进阶Java笔试面试Java面试宝典

面试系列之IO模型

2022-01-18  本文已影响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中的缓存区就是为了解决这些问题,每次读写都将数据暂存到缓存区然后在批量刷盘,而且还能够自由的前后移动读取缓存中的数据,增加了数据处理的效率和灵活性。

上一篇下一篇

猜你喜欢

热点阅读