一、如何理解Java中的I/O同步、异步、阻塞、非阻塞?

导论:什么是I/O,I(input),O(output),也就是输入和输出。这个输入和输出是相对而言的,是相对于CPU和内存而言的,我们把数据从外部输入到内存中,或者我们把数据从内存中持久化到硬盘,或者通过网络传输到另外一台设备中,这些操作就是IO操作,其中数据从外部介质输入到内存中,我们就称为I,而数据输出到外部介质中,我们称为O。

内存条,我们都懂得,特别是计科,软工的同学很多都自己安装过内存条。这个看得到的内存是物理内存,物理内存是看得到的,是实的,虚虚实实,那么肯定也有虚内存,也就是虚拟内存,我们把内存可虚拟划分为内核内存,用户内存,其中用户内存有的书中也称为进程内存。

这里假设,我们有两个进程,从下图中我们可以看出,把一块物理内存虚拟成多个内存,有进程共享内存空间,有共享内核空间,有进程独立的空间。注:此图来源于网络。

image-20230326113321721

二、阻塞和非阻塞

应用程序的IO实际是分为两个步骤,IO调用和IO执行。IO调用是由进程发起,IO执行是操作系统的工作。如果当数据没有准备好时,那么根据当前线程是否等待可以分为阻塞和非阻塞,如果当前线程一直在等待数据准备好,那么这个过程就是阻塞的。如果当前线程没有等待数据准备好,而是做自己的事情去了,那么这个过程就是非阻塞的。那么在非阻塞中我们怎么可以确定数据是否准备好呢?有两种方式,一种是通过轮询的方式,每隔一段时间询问内核空间是否准备好了数据,如果准备好了,那么就发送一个信号给当前进程,然后CPU发送指令,把内核空间中的数据复制到用户空间中。还有一种方式是通过信号,当前进程执行IO之后,会注册一个信号,当数据准备好了之后,CPU主动把内核空间中的数据,复制到用户空间中,也就是不用使用轮询的方式了。具体内容可以看参考文件1

image-20230326120038552

上图是同步阻塞的,下图表示的是同步非阻塞的。

image-20230326120158659

从上面两图可以看出,阻塞和非阻塞的区别在于内核中是否一直等待数据准备好,要等待,不去做其它的事情,那么当前线程就是阻塞的,如果不等待,而去做其它事情了,那么就是非阻塞的。

举个例子:周五晚上,你开开心心给女朋友发微信问:你在干嘛,一起出去吃个饭。然后女朋友回复了一句:我在洗澡。

你接下来是一直在等待呢,还是去做自己的事情呢?

假如你一直在等待对方回复消息,那么你就是阻塞的状态。

假如你是在做其它的事情,比如正在看我的这篇博文,并且你还会时不时的瞄一眼手机,生怕错过女朋友的消息。那么这个时候你就是非阻塞的状态。

通过上面的例子,你是不是更加理解什么是阻塞和非阻塞了呢?那么接下来,我们看看什么是同步和异步。

三、同步和异步

同步和异步说的是在内核拷贝到用户空间的过程,线程是否执行其它的操作,如果没有执行其它操作,那么我们把它称为同步的,否则我们就把它称为异步的。

image-20230326121951920

上面这个图是异步的,因为在并且是非阻塞的。

四、总结

同步、异步,阻塞、非阻塞是两两成对,根据排列组合,可以组合出4种情况,同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。但是我们实质上是3种,同步阻塞,同步非阻塞,异步非阻塞,这对应着 Java 中的 BIO,NIO,AIO。

综上所述可以看出时期不同,称呼不同,在内核空间中等待数据准备好的这个时期是否等待,分为阻塞和非阻塞,在内核空间的数据复制到用户空间的时期,我们根据当前线程是否等待划为同步和异步。

那么最后你和女朋友约会成功了吗?你在这个过程中是属于什么IO呢?

Q.E.D.


热爱生活,热爱程序