一文搞清楚Java中常见的IO模型

2023年 10月 12日 55.0k 0

什么是IO

首先,我们要清楚什么是IO,根据冯诺依曼结构,计算机结构分为5部分:运算器、控制器、存储器、输入设备和输出设备。在这里插入图片描述
输入设备和输出设备都属于外设,网卡、硬盘这种既可以属于输入设备也可以属于输出设备。
输入设备向计算机输入数据,输出设备接收计算机输出的数据。
从数据结构的时间来看的话,IO描述了计算机系统与外部设备之间通信的过程。
从应用程序的角度来看:
为了保证操作系的稳定性和安全性,一个进程的地址划分为用户空间和内核空间。
我们平时运行的程序都是在用户空间,只有内核空间才能进行系统态级别的资源有关的操作,比如文件管理,进程通信,内存管理等。也就是我们进行IO操作,一定要有依赖内核空间的能力。并且,用户空间的程序不能直接访问内核空间。
当要执行IO操作的时候,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。
因此,用户进程想要执行IO操作的话,必须通过系统调用来简介访问内核空间。我们平常开发种接触最多的就是磁盘IO(读写文件)和网络IO(网络请求和响应) 。
从应用程序角度来说,我们应用程序对操作系统内核发起IO调用(系统调用),操作系统负责的内核执行具体的IO操作。也就是说我们的应用程序实际上只是发起了IO操作的调用而已,具体的IO执行时由擦欧总系统完成的。
当IO发起待用时,会经历两个步骤:

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

下面来介绍一些基本概念:

阻塞非阻塞 指的是在客户端

  • 阻塞: 意味着 客户端提出一个请求以后,在得到回应之前,只能等待
  • 非阻塞: 意味着 客户端提出一个请求以后,在得到回应之前,客户端还可以做其他事情,可以继续提其他请求
    同步异步 指的是服务器端
  • 同步:意味着 服务器接受一个请求后,在返回结果以前不能接受其他请求
  • 异步:意味着 服务器接受一个请求后,尽管还没有返回结果,但是可以继续接受其他请求

举个例子
珂珂领着女朋友牛牛去超市购物,买了很多东西,当他走到收银员那里结账的时候,珂珂(客户端)发出了要求结账的讯息(请求),收银员(服务器)会对他这一要求进行处理。此时有可能产生多种场景

  • 珂珂傻傻地等着收银员用计算器算出所有物品的总价,并准备付款。(同步阻塞)
  • 珂珂觉得自己太傻了,于是一边和女朋友牛牛聊天,一边催促收银员快点计算出总价。(同步非阻塞)
  • 珂珂傻傻地等着收银员的总价结果,收银员却把计算的工作交给计算机之后就去拿袋子帮忙装东西,直到计算机上出现了总价结果,收银员才继续回来完成收款工作。(异步阻塞)
  • 珂珂觉得自己太傻了,于是一边和女朋友牛牛聊天,一边催出收银员快点计算出总价,而收银员却把计算的工作交给计算机之后就去拿袋子帮忙装东西,直到计算机上出现了总价结果,收银员才继续回来完成收款工作。(异步非阻塞)
  • 此时的同步异步,指的是收银员是否在处理收款这一请求的过程中去做了其他的事情,这也导致了收款的结果是当时告诉了珂珂,还是之后又进行了额外的通知。

    而阻塞非阻塞,指的是珂珂是否在等待处理结果的过程中去做了其他的事情。

    那么因此,就能得出结论:

    同步和异步:关注的是被调用者是否会通过原调用通知调用者。换句话说,处理请求者是通过原调用将结果返回,还是通过其他方式将结果通知调用者。

    阻塞和非阻塞:关注的是调用者是否会一直等待被调用者的通知。换句话说,发出请求者是否会在等待过程中去做别的事情。

    简单的记忆方法
    同步阻塞:A调用B,然后A一直等待B的返回;B执行完后通过原调用接口返回结果。
    同步非阻塞:A调用B,然后A执行其他操作,隔段时间看看原调用接口是否有返回结果;B执行完后通过原调用接口返回结果。
    异步阻塞:A调用B,然后A一直等待B的回调;B执行完后通过回调、状态等其他方式通知A结果。
    异步非阻塞:A调用B,然后A继续做别的,不再搭理B;B执行完后通过回调、状态等其他方式通知A结果。

    了解了这些,我们再说Java中有哪些常见的IO模型吧

    1. 同步阻塞IO(BIO)

    在BIO中,应用程序发起read调用后,会一直阻塞,知道在内核把数据拷贝到用户空间
    在这里插入图片描述
    我们经常用的IO比如:InputStream 和 OutputStream、Reader 和 Writer、
    Socket 编程、File I/O。
    BIO 模型的优点是简单易懂,适用于一些并发要求不高的场景。但它也有明显的缺点,主要体现在并发性能上:

    • 阻塞:BIO 的阻塞特性限制了服务器的并发性能,因为每个连接都需要一个线程来处理,当连接数较多时,线程数量也会增多,占用大量系统资源。
    • 扩展性差:由于每个连接都需要一个独立的线程,线程数量受限于操作系统和硬件资源,难以实现高并发。
    • 高开销:线程的创建和销毁以及线程切换都会带来额外的开销,降低了系统的性能。

    为了解决 BIO 模型的性能问题,后续发展了 NIO(New I/O)和 AIO(Asynchronous I/O)等模型,它们采用了非阻塞和异步的方式来提高并发性能。因此,在高并发和性能要求较高的应用中,通常会选择使用 NIO 或 AIO 模型来替代传统的 BIO 模型。

    2. 同步非阻塞IO(NIO)

    同步非阻塞 IO 模型中,应用程序会一直发起 read 调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。
    在这里插入图片描述
    NIO(New I/O,也称为 Non-blocking I/O)是 Java 中用于进行非阻塞式 I/O 操作的一种编程模型。它在 Java 1.4 版本引入,提供了一种更高效处理 I/O 操作的方式,特别适用于需要处理大量并发连接的网络应用程序。以下是常见的 NIO 组件和类:

    • Channels(通道): NIO 提供了一种新的通道抽象,它是双向的,可以支持读和写操作。常用的通道包括:
    • FileChannel:用于文件操作,支持文件的读写和定位操作。
    • SocketChannel:用于网络套接字的读写。
    • ServerSocketChannel:用于监听传入的 TCP 连接请求。
    • DatagramChannel:用于 UDP 数据报的读写。
    • Buffers(缓冲区): NIO 使用缓冲区来读写数据。常见的缓冲区包括:
    • ByteBuffer:用于读写字节数据。
    • CharBuffer:用于读写字符数据。
    • ShortBuffer、IntBuffer、LongBuffer:用于读写不同数据类型的数据。
    • Selectors(选择器): 选择器是 NIO 的核心组件,用于多路复用 I/O 通道。它可以监视多个通道

    NIO 的主要特点是非阻塞和事件驱动,能够更高效地处理大量并发连接,适用于构建高性能的网络服务器和大规模的数据传输应用。与传统的 BIO 模型相比,NIO 更适合需要处理大量连接的场景,如 Web 服务器、聊天服务器等。

    3. IO多路复用(AIO)

    异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
    在这里插入图片描述
    目前来说 AIO 的应用还不是很广泛。Netty 之前也尝试使用过 AIO,不过又放弃了。这是因为,Netty 使用了 AIO 之后,在 Linux 系统上的性能并没有多少提升。

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论