I/O
什么是I/O?
I/O即输入(Input)输出(Output)
从计算机结构角度来看:
计算机有输入设备(键盘等)和输出设备(显示屏等),(网卡和硬盘既可以作为输入也可以作为输出)。
所以从计算机结构角度来看,I/O描述的是计算机系统与外部系统的通信。
从应用程序角度来看:
为了保证操作系统的安全性和稳定性,一个进程的地址空间被分为用户空间和内核空间。
我们平时的应用程序都是运行在用户空间,而像文件管理、进程通信、内存管理、I/O等都是需要依赖内核空间来完成的。
而我们要执行I/O操作时,由于我们没有权限,所以只有发起系统调用,让操作系统来帮我们完成。而我们一般开发常见的就是磁盘I/O(读写文件)和网络I/O(网络请求和响应)。
所以从应用程序来看,I/O是指我们的应用程序对操作系统内核发起I/O调用(系统调用),操作系统来负责具体的I/O,将我们需要的数据从内核空间拷贝到用户空间。
常见的I/O模型
UNIX系统下的五种I/O模型
- 同步阻塞I/O
- 同步非阻塞I/O
- I/O多路复用
- 信号驱动I/O
- 异步I/O
Java中3种常见I/O模型
BIO(Blocking I/O)
同步阻塞I/O
BIO在应用程序发起read调用后,会一直进入阻塞,直到系统把数据从内核拷贝到用户空间为止。
BIO适用与客户端连接数量不多的情况下,如果连接达到十万级甚至百万级,无法适用BIO
NIO(Non-blocking/New I/O)
而Java中的NIO是同步非阻塞I/O
还是I/O多路复用
呢?
Java中的NIO可以看作I/O多路复用
先看看同步非阻塞I/O
应用程序会通过轮询的方式反复像内核发起read调用,直到内核把数据拷贝到用户空间。通过轮询虽然避免了阻塞,但是轮询操作十分消耗CPU资源。
所以Java选择了I/O多路复用
首先应用程序会发起一个select调用来询问数据准备好没有,如果准备好了才会发起read调用,通过这种方式减少了无效的系统调用,减少了对CPU的消耗。
NIO中引入了三个重要的概念selector
、channel
、buffer
;
channel:
-
读写数据的双向通道
-
从buffer读入数据,写出数据到buffer
buffer:
- 缓冲读写数据
selector:
- 通过一个selector可以管理多个channel,当客户的数据到了之后,才会为其服务。
(图中channel拼写错误)
AIO(Asynchronous I/O)
异步非阻塞I/O
AIO的异步是通过事件和回调机制来实现的,应用程序发起调用后会直接返回,不会等待,当后台处理完后,会通知相应的线程继续处理。
但AIO的应用还并不广泛,具体原因如下
- 兼容性:AIO 是在 JDK1.7 中引入的新特性,因此只有使用 JDK1.7 或更高版本的应用程序才能使用 AIO。在实际应用中,许多企业应用程序仍然在使用较老版本的 JDK,因此 AIO 的兼容性是一个问题。
- 学习成本:相比于传统的阻塞 I/O 模型和 NIO(非阻塞 I/O)模型,AIO 模型的编程难度更高,需要花费更多的时间和精力学习和理解。这使得一些开发者可能不太愿意采用 AIO。
- 稳定性:虽然 AIO 能够提供高吞吐量和低延迟,但在处理高并发请求时,其稳定性可能会降低。而且不同操作系统的实现效果也可能不同,这也是 AIO 应用不广泛的原因之一。
用 AIO。 - 稳定性:虽然 AIO 能够提供高吞吐量和低延迟,但在处理高并发请求时,其稳定性可能会降低。而且不同操作系统的实现效果也可能不同,这也是 AIO 应用不广泛的原因之一。
- 应用场景受限:AIO 主要适用于 I/O 密集型应用程序,这也限制了 AIO 应用的范围。对于计算密集型应用程序,AIO 并不一定能够提供更好的性能表现。