BIO:同步阻塞 IO
NIO:同步非阻塞 IO
AIO:异步非阻塞 IO
关于阻塞和非阻塞
不管是文件 IO 还是网络 IO,会阻塞的根本原因在于应用程序的用户空间和操作系统的内核空间的数据互相拷贝
文件 IO
读取:需要将操作系统内核空间将数据准备好拷贝给应用程序的用户空间
写入:需要将应用程序的用户空间将数据准备好拷贝给操作系统内核空间
网络 IO
接收网络请求:网络 –》网卡 –》内核空间 –》用户空间
发送网络请求:用户空间 –》内核空间 –》网卡 –》网络
阻塞 IO
用户线程必须等待用户空间和内核空间之间的互相拷贝完成才能执行下一步
非阻塞 IO
用户线程把请求提交给用户空间后不需要一直等着用户空间和内核空间完成互相拷贝完,它可以继续执行,当用户空间和内核空间完成互相拷贝之后通知用户线程获取数据
BIO 与 NIO 区别
BIO 通常不知道什么时候能够读写,并且读写线程不能共用,只能傻等,最好的方式就是起线程另起炉灶,但在 linux 中线程本质是进程创建、销毁线程的代价很高昂,如果线程数量过高,会导致线程切换的时间高于线程执行时间,带来的现象就是系统的 load 偏高导致系统几乎不可用,容易造成锯齿状的系统负载,因为系统负载是用活动线程数或 CPU 核心数,一旦线程数量高但外部网络环境不是很稳定,就很容易造成大量请求的结果同时返回,激活大量阻塞线程从而使系统负载压力过大。
NIO 本质上是通过统一的 Selector 管理(通过注册需要的读写,从 channel 通道中轮询获取请求)达到单线程就能处理不同的读写操作,从而达到减少线程创建、销毁的数量,增加 cpu 的有效利用率(实际执行 IO 读写,而不是线程切换)
NIO 与 AIO 的区别
从编程模式上来看 AIO 相对于 NIO 的区别在于,NIO 需要使用者线程不停的使用 Selector 轮询所有的 IO 对象,来确定是否有数据准备好可以读了,而 AIO 则是在数据准备好之后,才会通知数据使用者(专门的 Selector 线程来轮询),这样使用者就不需要用 Selector 线程不停地轮询了。当然 AIO 的异步特性并不是 Java 实现的伪异步,而是使用了系统底层 API 的支持,在 Unix 系统下,采用了 epoll,IO 模型,而 windows 便是使用了 IOCP 模型
不同平台下采用轮询的方式
Linux 下 NIO、AIO 采用 Linux 的 epoll 来轮询,windows 下 NIO、AIO 采用 IOCP 的形式轮询