Java I/O模型
Java I/O模型
I/O需要解决的问题
I/O:在计算机内存与外部设备之间拷贝数据的过程。
由于CPU访问内存的速度是远远高于访问外部设备的,所以CPU会优先将外部设备的数据读取到内存后,再处理。
程序通过CPU向外部设备发出读指令,数据从外部设备拷贝至内存需要一段时间,这段时间CPU就没事情做了,程序就会两种选择:
- 让出CPU资源,让其干其他事情
- 继续让CPU不停地查询数据是否拷贝完成
到底采取何种选择就是I/O模型需要解决的事情。
各种I/O模型的不同
以一个网络I/O通信过程来说,如网络数据读取,会涉及两个对象,一个是调用这个I/O操作的用户线程,另一个是操作系统内核。一个进程的地址空间分为用户空间和内核空间,用户线程不能直接访问内核空间。
即当用户线程发起I/O操作后,网络数据会经历2个步骤:
- 用户线程等待内核将数据从网卡拷贝到内核空间
- 内核将数据从内核空间拷贝至用户空间
不同的I/O模型对于这2个步骤有着不同的实现步骤。这就是各I/O模型的不同之处。
I/O模型
在UNIX系统中,有5中I/O模型:同步阻塞I/O、同步非阻塞I/O、I/O多路复用、信号驱动I/O和异步I/O,除了很少使用的信号驱动I/O,其他4种I/O模型是实际中使用的。
-
同步阻塞
用户线程发起read调用后就阻塞了,让出CPU。内核等待网卡数据到来,把数据从网卡拷贝到内核空间,再把数据从内核空间拷贝到用户空间,再把用户线程唤醒处理数据。 -
同步非阻塞
用户线程不断地发起read调用,数据没到内核空间时,每次都返回失败,直到数据到了内核空间,这次调用read后,在等待数据从内核空间拷贝到用户空间这段时间里,线程还是阻塞的,等数据到了用户空间后,再把线程唤醒。
-
I/O多路复用
用户线程的读取操作分成2步,线程先发起select调用,目的是问内核数据是否准备好了,等内核数据将数据准备(拷贝)好,用户线程再发起read调用。在等待数据从内核空间拷贝到用户空间这段时间里,线程还是阻塞的。之所以叫I/O多路复用,是因为一次select调用可以向内核查多个数据通道(channel)的状态。
-
异步I/O
用户线程发起read调用的同时注册一个回调函数,read立即返回,等内核将数据准备好后,再调用指定的回调函数完成处理。在这个过程中用户线程一直没有阻塞。