详解java以及操作系统的IO模型

文章目录

    • java IO模型和Linux操作系统IO模型的关系
    • 操作系统IO模型
      • 1、阻塞IO模型
      • 2、非阻塞IO模型
      • 3、多路复用IO模型
      • 4、信号驱动IO模型
      • 5、异步IO模型
      • 6、小结
    • java中的IO模型
      • 1、BIO
      • 2、NIO
        • channel
        • buffer
        • selector
      • 3、AIO
    • 参考

java IO模型和Linux操作系统IO模型的关系

java中的IO模型其实就是对底层操作系统IO操作的封装,只是提供的一种API,在java中调用Socket或者ServerSocket,最终都会调用底层的内核操作。例如:java中的NIO其实是对底层操作系统【非阻塞IO模型】和【多路复用IO模型】的一种封装。
所以,先来康康操作系统IO模型都有哪些?

操作系统IO模型

为了便于理解,我们利用生活中的【取快递】来分析下面这几种IO 模型。

1、阻塞IO模型

早上去楼下取快递,结果快递晚上才到,一整天啥事没干,光等快递了。

映射到IO模型中就是,线程在内核还没准备好数据之前,会一直处于阻塞状态,不能执行其他操作。而且每个IO请求都需要创建一个线程去处理,增加线程频繁切换开销。这种IO模型最简单,对于一些并发量不大的场景可以使用,但无法承受高并发。

2、非阻塞IO模型

早上去楼下取快递,结果快递还没到,好吧不等了,先回去刷会儿剧,到广告时间了,再下去瞅一眼快递到没到,没到继续回去刷剧。

映射到IO模型中,就是线程不用再阻塞等待内核准备好数据了,而是轮询等待内核什么时候准备好再执行后续操作,在这段空闲时间里线程就可以干其他操作了。
从例子也可以看出来,虽然这种方式不用阻塞了,但还是很烦啊,我看剧看的好好的,还得惦记着下面的快递,时不时下楼一趟,多麻烦。
因为轮询非常消耗CPU资源,所以这种模型一般很少直接使用,而是在其他模型中使用非阻塞这一特性

3、多路复用IO模型

正如上面说的,虽然不用一直等快递了,但是这么热的天不断跑上跑下看快递到没到也是很心塞,所以我雇了门卫大爷帮我监视着快递员什么时候到了,再通知我。

多路复用模型是建立在内核提供的多路分离函数select基础之上的,应用程序首先将IO请求注册到select上,然后阻塞等待select上至少有一个IO请求有数据到达时返回,此时应用线程再去读取数据。使用select函数可以避免同步非阻塞IO模型中轮询等待的问题,但是感觉和同步阻塞模式很像,因为应用程序在等待数据的过程中依然是阻塞的,而且还添加了一些额外的调用select函数的操作,性能貌似更低了。
但其实多路复用最大的优势在于,select允许应用程序注册多个socket,这样就实现了通过一个线程处理多个IO请求的功能。而同步阻塞模式对于这种场景必须采用多线程才能实现。

其实大多数使用了IO多路复用模型的应用,都提供了非阻塞NonBlock特性,例如:Reids、Netty等。
而且通常是采用Reactor模式(事件响应机制)实现的非阻塞。
多路复用实现机制通常有三种:select、poll、epoll

4、信号驱动IO模型

快递到了之后会打电话,我接到电话之后才下楼去取。
这种IO模型就是应用程序向内核中注册一个信号处理函数,等到内核准备好数据之后,就向应用程序发送一个信号,此时应用程序就可以去拷贝数据了。

【注】上面四种IO模型都是同步IO模型,无论是阻塞还是非阻塞,最后都需要我自己去取快递,也就是应用程序都需要自己去读写数据,所以是一种同步模型。

5、异步IO模型

异步就是取快递这件事我完全不用操心 了,由代理直接取回来放在家门口,我只负责拆快递了。
也就是说应用程序只需要发起一个IO请求,剩下的数据准备、数据拷贝(读写)过程都由操作系统完成,内核只需要在完成的时候通知应用程序即可。

6、小结

应用程序发起的IO操作,大体可以分为两步:一、等待内核准备数据,二、将内核准备好的数据拷贝到用户空间。
阻塞IO——两步操作都阻塞
非阻塞IO——第一步非阻塞,但是需要应用程序不断轮询,第二步阻塞
信号驱动模型——第一步非阻塞,第二步阻塞
多路复用模型——两步操作都阻塞,但是可以复用一个线程处理多个IO请求
异步IO——两步都非阻塞

java中的IO模型

java中的IO模型本质上就是封装底层操作系统IO模型的API。
java一共实现了三种IO模型:

1、BIO

java中的BIO是对底层操作系统阻塞IO模型的封装,也是java最早提供的一种io方式,具体实现放在io包下。

2、NIO

java中的NIO是基于channel(通道)、buffer(缓冲区)、selector(选择器)实现的一种非阻塞的IO多路复用模式。

channel

channel类似于传统IO中的InputStream和OutputStream流,可以理解为是一种读取数据的方式,或者IO连接。

InputStream inputStream = new FileInputStream(file);
inputStream.read(new byte[1024]);

那么channel和传统的读取数据方式有什么不同呢?
(1)通道是双向的,既可以执行读操作,也可以执行写操作,而stream只能进行单向操作,例如:InputStream只能执行读操作。
(2)通道的读写是基于buffer缓冲区的,即通道中的数据总是要先读到一个缓冲区,或者总是要从一个缓冲区中读入。而传统io是直接在流上读写数据的。
(3)正是因为缓冲区的使用,通道可以实现异步读写。例如:从通道进行数据读取时,首先创建一个缓冲区,然后请求通道读取数据到buffer,等到数据读取到buffer之后,线程再从buffer中获取数据。对通道执行写入操作时,首先创建一个缓冲区,线程向buffer中填充数据,然后请求通道写数据。

常用的几种channel实现:
SocketChanel/ServerSocketChannel:以TCP读写网络传输的数据
FileChannel:读写文件数据
DatagramChannel:以UDP读写网络数据

buffer

buffer就是一个缓冲区,用来实现线程异步读写数据。
常用的网络读写缓冲区是ByteBuffer。
详解java以及操作系统的IO模型_第1张图片
读请求中的第2步,是从通道读取数据到buffer中(磁盘到内存),用户线程不需要阻塞;
写请求中的第3步,是将填充到buffer中的数据写入通道(内存到磁盘),用户线程不需要阻塞。

selector

selector其实就是对多路复用IO模型中select的封装。用来监听注册在selector上的多个channel是否有事件到达,一旦有事件到达,线程就可以开始读写数据了。

3、AIO

AIO是jdk1.7新增的一种异步IO方式,具体是通过nio包中的几个异步通道实现的:
AsynchronousFileChannel
AsynchronousServerSocketChannel
AsynchronousSocketChannel

参考

[1] https://blog.csdn.net/sehanlingfeng/article/details/78920423
[2] https://mp.weixin.qq.com/s?
__biz=Mzg3MjA4MTExMw==&mid=2247484746&idx=1&sn=c0a7f9129d780786cabfcac0a8aa6bb7&source=41&scene=21#wechat_redirect

===========================================================================

你可能感兴趣的:(java基础)