Java IO模型详解

Java IO模型详解

一、I/O的定义

I/O 是 Input/Output 的首字母缩写,即输入/输出,它描述的是数据流动的过程。输入/输出是相对而言的。下面将从两个角度出发来进一步理解 IO:

1、计算机结构的视角

在这里插入图片描述

根据冯·诺依曼结构,计算机分为五大部分,分别是:控制器、运算器、存储器、输入设备、输出设备。

输入设备(如鼠标键盘)和输出设备(如显示器)都属于外设(外部设备),而像硬盘、网卡这种既属于输入设备又属于输出设备。

从计算机的角度出发的话,操作系统将从输入设备读取到的数据写入到输出设备,这就是一次完整的 I/O 过程。

即 I/O 描述了计算机核心(CPU和内存)外部设备之间的数据转移的过程。

2、应用程序的视角

我们都知道,应用程序作为一个文件保存在磁盘中,只有加载到内存中成为一个进程才能够运行。

为了确保操作系统的安全性和稳定性,操作系统会将内存分为 内核空间用户空间,进行内存隔离。

而我们运行的应用程序都是运行在用户空间的,只有内核空间才能进行系统态级别的资源有关的操作,比如文件管理、进程通信、内存管理等。也就是说,我们想要进行 I/O 操作,就必须依赖内核空间的能力。但是,用户空间的程序是无法直接访问内核空间的。

这时我们就需要通过发起系统调用请求操作系统帮忙完成,所以应用程序想要执行 I/O 操作的话,必须通过调用内核提供的 系统调用 进行间接访问。

我们在平常开发过程中接触最多的就是 **磁盘 I/O(读写文件)**和 网络 I/O(网络请求和响应)

从应用程序的角度出发的话,我们的应用程序对操作系统的内核发起 I/O 调用(系统调用),操作系统负责的内核执行具体的 I/O 操作。即强调的是通过向内核发起系统调用完成对 I/O 的间接访问。

上述过程换句话说即一次 I/O 操作实际上包含两个阶段:

  • I/O 调用阶段:应用程序进程向内核发起系统调用
  • I/O 执行阶段:内核执行 I/O 操作并返回
    • 内核等待 I/O 设备准备好数据
    • 内核将数据从内核空间拷贝到用户空间

在这里插入图片描述

二、Java 中3种常见的 I/O 模型

  • 同步 I/O, 是用户线程发起 I/O 请求后需要等待或轮询内核 I/O 操作完成后才能继续执行。
  • 异步 I/O,异步 IO 是用户线程发起 IO 请求后可以继续执行,当内核 IO 操作完成后会通知用户线程,或调用用户线程注册的回调函数。
  • 阻塞 I/O,阻塞是指用户空间程序的执行状态,用户空间程序需等到 I/O 操作彻底完成。传统的 I/O 模型都是同步阻塞 I/O。在 Java 中,默认创建的 socket 都是阻塞的。
  • 非阻塞 I/O,是指用户程序不需要等待内核IO操作完成后,内核立即返回给用户一个状态值,用户空间无需等到内核的 I/O 操作彻底完成,可以立即返回用户空间,执行用户的操作,处于非阻塞的状态。

同步和异步是通信机制,阻塞和非阻塞是调用状态。

1、同步阻塞 I/O(BIO)

应用程序中进程在发起 I/O 调用后至内核执行 I/O 操作返回结果之前,若发起系统调用的线程一直处于等待(阻塞)状态,则此次 I/O 操作为阻塞 I/O 。

阻塞 I/O 简称 BIO(Blocking IO)

在这里插入图片描述

如上图,同步阻塞 I/O 模型中,当用户线程发出 I/O 调用后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而此时用户线程也就会处于阻塞状态,用户线程交出 CPU 。当数据就绪之后,内核会将数据拷贝到用户空间,并返回结果给用户线程,用户线程才会接触阻塞状态。

  • BIO 的优点:

程序简单,在阻塞等待数据期间,用户线程挂起。用户线程基本不会占用 CPU 资源。

  • BIO 的缺点:

一般情况下,一个线程维护一个连接成功的 IO 流的读写。在并发量小时是没有问题的,但在并发很大时,BIO 是非常消耗系统资源的,这是行不通的。

2、同步非阻塞 I/O(NIO)

应用程序中进程在发起 I/O 调用后至内核执行 I/O 操作返回结果之前,若发起系统调用的线程不会等待而是立即返回,则此次 I/O 操作为非阻塞 I/O 模型。

非阻塞 I/O 简称 NIO(Non-Blocking IO)

在这里插入图片描述

如上图,同步非阻塞 I/O 模型中,当用户线程发起一个 read 操作后,并不需要等待,而是马上得到了一个结果。如果结果是一个调用失败的信息时,此时代表数据还没有准备好,他就可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么他就马上将数据拷贝给了用户线程,然后返回。

在同步非阻塞 I/O 模型中,用户线程需要不断地询问内核数据是否就绪,也就是说同步非阻塞 I/O 模型不会交出 CPU ,而会一直占用 CPU。

即应用程序的线程需要不断的进行 I/O 系统调用,轮询数据是否已经准备好,如果没有准备好,直到完成系统调用为止。

  • NIO的优点:

每次发起的 IO 系统调用,在内核的等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。

  • NIO的缺点:

需要不断的重复发起 IO 系统调用,这种不断的轮询,将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低。

★ I/O 多路复用模型

I/O 多路复用模型是目前使用得比较多的模型。Java 中的 NIO 可以看作是 I/O 多路复用模型(IO Multiplexing),而 Java 中的 NIO 可以通过的 Selector(选择器)达到一个线程管理多个客户端连接的效果。

IO 多路复用模型中,线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。read 调用的过程(数据从内核空间 -> 用户空间)还是阻塞的。即在 read 调用之前会有一个 select 调用

在这里插入图片描述

IO 多路复用模型,通过减少无效的系统调用,减少了对 CPU 资源的消耗。

目前支持 IO 多路复用的系统调用,有 select,epoll 等等。select 系统调用,目前几乎在所有的操作系统上都有支持。

  • select 调用 :内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持。
  • epoll 调用 :linux 2.6 内核,属于 select 调用的增强版本,优化了 IO 的执行效率。

3、异步非阻塞 I/O(AIO)

应用程序中在发起 I/O 调用后,用户进程立即返回,内核等待数据准备完成,然后将数据拷贝到用户进程缓冲区,最后发送信号告诉用户进程 I/O 操作执行完毕,则此次操作为异步 I/O。

异步 I/O 简称 AIO(Asynchronous I/O)

在这里插入图片描述

异步 I/O 真正实现了 I/O 全流程的非阻塞。用户线程完全不需要关心实际的整个 I/O 操作是如何进行的,只需要先发起一个请求,当接收内核返回的成功信号时表示 I/O 操作已经完成,可以直接去使用数据了。

4、BIO、NIO、AIO 适用场景

  • BIO方式 适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序简单易理解。
  • NIO方式 适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,弹幕系统,服务器间通讯等。编程比较复杂,JDK1.4开始支持。
  • AIO方式 使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

猜你喜欢

转载自blog.csdn.net/m0_53067943/article/details/128957719