【java】一文让你了解透彻Java中的IO模型

前言

本文只是说明了IO模型,让你了解IO模型是什么,怎么区分IO模型,以及分析了Java中的三种IO模型,没有讲述Java的IO操作,本文是纯理论知识,看完之后会让你对IO有更加深刻的理解,面试的时候就不会害怕了。

一、什么是IO

IO(Input/Output),也就是输入和输出的简称,从计算机结构的角度来看,IO,就是输入数据到计算机中,计算机输出数据到计算机外,下面有一张十分经典的冯·诺伊曼结构图,将计算机分为五大部分:运算器、控制器、存储器、输入设置、输出设备。
在这里插入图片描述

输入设备向计算机输入数据,输出设备接收计算机输出的数据。

所以,从计算机结构的角度来看IO,IO就是描述了计算机系统和外部设备之间通信的过程

接下来我们再从应用程序的角度去了解一下IO:在操作系统中,为了保证操作系统的稳定性和安全性,一个进程的地址空间被划分为了用户空间(User space)和内核空间(Kernel space)

那么什么是用户空间,什么是内核空间呢?

  • 用户空间是普通应用程序可访问的内存区域。
  • 内核空间是操作系统内核访问的区域,独立于普通的应用程序,是受保护的内存空间。

可以简单的理解为我们平常运行的应用程序,都是运行在用户空间中的,它没有权限去进行一些系统态级别的资源相关操作,比如文件管理、内存管理、等等,这些操作都是要依赖内核空间才能完成,也就是说,我们如果想要进行IO操作,一定是要依赖内核空间的能力,并且这里需要注意一点:用户空间的程序不能直接访问内核空间。

那么如果要发起一次IO操作,那应该怎么办呢?

当我们需要执行一次IO操作的时候,由于没有执行IO操作的权限,只能发起系统调用请求操作系统来帮忙完成,所以用户进程想要执行IO操作的话,必须通过系统调用来间接的访问内核空间,比如我们最常见的IO操作就是磁盘IO(读写文件)和网络IO(网络请求和响应)。

那么从应用程序的视角去看IO的话,我们的应用程序发起对操作系统的内核发起IO调用(系统调用,注意只是调用),操作系统去负责内核执行具体的IO操作。(如果这段话不是很好理解,可以理解为调包,比如别人写好了的工具类,我们不需要关注里面的实现细节,只需要调用它的方法就好了,这里我们的应用程序就是发起了一个调用,但是真正执行的还是我们的操作系统)

当应用程序发起IO调用之后,会经历下面两个步骤:(这里一定要记住这两点,它和后面息息相关!)

  • 内核等待IO设备准备好数据
  • 内核将数据从内核空间拷贝到用户空间

二、常见的IO模型

IO模型有很多种(比如在UNIX系统下,IO模型一共分为5种:同步阻塞IO、同步非阻塞IO、IO多路复用、信号驱动IO、异步IO),本篇文章只讲述Java中的IO模型,Java中的IO被分为三类:BIO、NIO、AIO。

BIO(Blocking IO)

BIO属于同步阻塞IO模型,应用程序发起read调用之后,会一直阻塞,直到内核把数据从内核空间拷贝至用户空间。
在这里插入图片描述

BIO就是Java中最传统的IO模型,相关的类和接口都在java.io这个包下面,因为现在用的人很少(后面看它的特点就知道为什么了),所以我们这里简单介绍一下它的一些特点就好了。

对高并发场景下对于线程资源的消耗较高,每一个连接需要使用一条线程单独处理。
传输较小对象时存在频繁的线程上下文切换等性能问题。

如何优化

那么怎么去优化这个BIO呢?

首先上面说了BIO的缺点之一就是它是有多少连接就需要多少线程的模型,但是对于用户来说,打开一个连接,然后关闭一个连接是十分常见且频繁的事情,而与之对应的就是创建线程和销毁线程,但是创建线程和销毁线程对于操作系统来说是十分消耗资源的,所以想到的优化就是使用线程池去进行优化BIO。

NIO的面世

但是BIO的模型决定了它的上限,它始终是同步阻塞的IO模型,阻塞就会导致不能使用单线程处理多个请求,所以这个时候就需要修改它的模型,将调用read()、write()方法不再是阻塞的,这样就可以使用单线程处理多个请求,而这样就不再是同步阻塞的IO模型了,也就是我们下面要说到的NIO了。

三、NIO(Non-blocking/New IO)

Java的NIO是Java1.4引入的,对应的是java.nio包,提供了channel、selector、buffer等抽象。
Java中的NIO可以看作是IO多路复用模型,但是也有人认为它是同步非阻塞IO模型,这里我们先简单介绍一下这两种模型:

同步非阻塞IO模型

在这里插入图片描述

这个相比同步阻塞IO模型,同步非阻塞IO模型就是通过轮询的方式,避免了一直阻塞。

但是从图中也能看出问题所在,就是应用程序不断的进行IO系统调用轮询数据是否已经准备就绪的这段时间,是十分消耗CPU资源的(说白话就是反复调用read操作) 。
所以这个时候就引入了IO多路复用模型。

IO多路复用模型

在这里插入图片描述

在IO多路复用模型中,线程首先发起select调用,询问内核数据是否准备就绪,等待内核把数据准备好了,会返回一个ready调用,告诉你,我准备好了,这个时候用户线程再发起read调用。注意:read调用的过程依然是阻塞的(数据从内核空间拷贝至用户空间这段时间) 。

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

Java中的NIO

Java中的NIO,有一个十分重要的概念,就是选择器(selector),也被称为多路复用器,通过它就可以实现使用一个线程管理多个客户端连接(这里是不是和BIO就不一样了,最大的优化的点就在这里) 。当客户端数据到了之后,线程再为客户端进行服务。
下面给出一张JavaNIO的图:
在这里插入图片描述

这张图就能很好的说明了NIO的特点了。

四、AIO(Asynchronous IO)

AIO,也被称为NIO 2.0,Java7中引入的异步IO模型,很多人都不理解非阻塞IO和异步IO到底有什么差别,其实异步IO就是基于事件和回调机制去实现的,也就是说用户调用操作之后会立马返回,不会阻塞,当后台处理完成,操作系统会通知相应的线程进行后续操作。
在这里插入图片描述

目前来说AIO的应用还没有十分广泛,应用最多的是NIO。

总结

最后放一张图来总结Java中的IO模型:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u011397981/article/details/130480068