JavaNIO——零基础学习NIO前必须要了解的事
更新: 5/9/2025 字数: 0 字 时长: 0 分钟
特别感谢
到底什么是Java AIO?为什么Netty会移除AIO?一文搞懂AIO的本质!
在最早学习IO的时候,对于IO的很多内容就处于一知半解的状态,尤其在后期学习Netty时,对于IO的不理解更是导致了我几乎无法理解Netty到底做了些什么,为什么要这样做,直到最近学习八股,深入的了解了一下IO于基础后,我才懵懂的明白了之前学的东西究竟是什么......
IO与计算机
在学习计算机组成原理时我们曾学过IO,那时我们的IO说的是输入输出设备,用于计算机与外部世界沟通。
而在学习操作系统的时候我们又曾学过,为了保护系统的安全性,我们将系统空间区分为用户空间与内核空间。我们平常的应用程序(包括我们的Java应用)都是运行在用户空间,而内核空间运行的则是系统态级别的资源有关的操作,比如文件管理,进程通信等。
因此我们不难发现——我们的应用其实无法直接沟通IO设备!!!
我们的应用如果想要完成IO操作就必须向操作系统发起请求,操作系统在接受到我们的请求后进而帮我们去完成我们想要完成的操作,这一过程会经历两个步骤。
- 内核等待 I/O 设备准备好数据
- 内核将数据从内核空间拷贝到用户空间。
Java中的IO模型
BIO(同步阻塞IO)
该类IO即我们平时用的InputStream,Reader等,会直接向操作系统发起请求,然后阻塞在那里等待操作系统准备好数据然后拷贝到我们的用户空间,由于阻塞的原因,这类IO在十万甚至百万级连接的时候会显得无能为力。
NIO(非阻塞IO)
Java1.4中引入NIO,在java.nio包下。
NIO采取的是IO多路复用模型,再该模型的设计下,线程首先会发起一个select调用,该调用会询问内核数据是否已经准备好,若准备好才会发起read调用将数据从内核空间拷贝到内核空间。
我们不难发现,NIO实际上是省去了BIO中等待操作系统准备数据的过程,仅在数据准备好时再去执行read操作,但read的这一过程(数据从内核空间 -> 用户空间)是阻塞的
AIO(异步IO)
AIO又被称为NIO2.0,Java7中正式引入AIO。
AIO基于事件和回调机制实现,采用异步操作来完成IO调用,当发起read调用后,会立刻返回,不会阻塞在那里,从准备—>准备完成—>拷贝
这一过程全部由后台完成,仅在拷贝完成后通知线程该操作已经完成,可以进行后续操作。
AIO目前并未被大量使用,若想深入了解AIO,建议阅读得物技术团队编写的到底什么是Java AIO?为什么Netty会移除AIO?一文搞懂AIO的本质!
为什么阻塞无法应对大量请求
产生这种疑问主要还是对线程阻塞不太理解。
阻塞的是线程,挂起的是任务
当线程阻塞时,当前线程无法执行新到来的任务,这时如果到来一个新的任务且没有空闲的线程,那么我们只能新起一个线程,但是资源不是无限的,因此我们应该尽可能避免线程的阻塞。
BIO,NIO,AIO到底谁是异步的?
首先我们得先定义什么叫异步,引用得物技术团队文章中的定义,我们认为完成IO操作的线程和发起IO操作的线程不是一个线程就是异步的。
而只有AIO使用事件和回调机制实现了这个方案,让程序员提前定义好IO操作成功与失败时的操作,然后当前线程继续后续代码,等IO完成使用新的线程执行我们提前定义好的回调方案。
NIO
NIO的核心为Buffer,Channel,Selector,我们接下来一次来说一下这些东西。
Buffer(缓冲区)
在BIO的学习中我们就常用BufferedInputStream来优化我们的InputStream,而NIO更是进一步使用了Buffer,NIO的数据在写入Channel前全部需要经过缓冲区。
Channel(通道)
在BIO中我们的数据是单向的,分为输入流和输出流,但NIO中使用Channel来完成双向的任务(之所以叫Channel可能也是因为通道不限制水的流向,仅提供一个场地)
Channel映射了操作系统的IO操作,连接了文件与Buffer,进而让我们可以实现文件的读写。
Selector(选择器)
Selector是NIO中多路复用模型的核心,正是Selector实现了一个线程可以处理多个 Channel。
我们先将Channel注册到Selector上,然后Selector去轮询每个Channel。当Channel上有新的 TCP 连接接入或是读和写事件时,当前Channel 就处于就绪状态,会被 Selector 轮询出来。
通过SelectionKey可以获取就绪Channel的集合,然后对这些就绪的Channel进行相应的操作。