前言
学好IO是java程序员从初级到高级进阶学习的必经之路,像Netty这种优秀网络框架是必须要学习的,说到Netty就离不开java的IO模型,我们知道BIO、NIO、AIO分别是同步阻塞IO、同步非阻塞IO、异步非阻塞IO,那么这里说的同步和异步、阻塞和非阻塞指的是什么呢?
同步和异步
在java IO世界所说的同步和异步是一个针对用户态和内核态的概念,用户程序要想发送数据要经过下面的步骤:
- 应用程序将要发送的内容写入用户内存空间
- 应用程序向操作系统内核发送系统调用
- 操作系统内核将用户空间中的数据读取到内核缓冲区
- 操作系统通知网卡读取内核缓冲区的数据,此时CPU可以处理其他事
- 网卡从指定的内核空间读取数据
- 网卡将数据转换成二进制形式,以比特流的形式传输
ps:下面说的用户进程等同于应用程序,是同一个概念
在这个过程中才有同步异步的概念:
同步和异步是针对应用程序和内核的交互而言的.
同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪。
异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。
同步和异步的主要区别就是在用户程序通过系统调用IO操作时是否需要等待或者轮询,以便查看操作系统是否完成了IO操作。
这里的等待IO指的是数据从应用程序拷贝到内核空间(或从内核空间拷贝到用户程序)的过程。( 出于系统安全考虑,用户线程是没法直接读取内核态内存的。)
阻塞和非阻塞
阻塞和非阻塞是针对于应用程序在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,阻塞方式下读取或者写入过程将一直等待结果,而非阻塞方式下,读取或者写入过程会立即获得一个返回值。
简单来说就是阻塞情况下需要等待执行结果,非阻塞情况下立即获得结果(不管最终操作成功还是失败)。结合同步异步过程理解,
阻塞:当用户线程发起一个IO请求的时候,请求会到达内核,内核获取到数据后,将数据从内核空间拷贝到用户空间。期间,当内核未将数据准备好时,用户线程一直处于阻塞状态(用户线程不会获得反馈结果)
非阻塞:当用户程序发起IO请求时,不用等到内核准备好数据才能返回,而是可以立刻返回(不论什么状态都获得反馈结果),没有被阻塞住。
java中的IO
- 同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!
- 同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后可立即返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。
- 异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!(关于select函数不是本文重点)
- 异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。