IO模型(Java)

io模型本是UNIX网络编程相关的内容。UNIX中IO模型有五种,包括阻塞 I/O、非阻塞 I/O、I/O 复用、信号驱动式 I/O 、异步 I/O 等。

 阻塞 I/O 模型

阻塞式IO模型,一般表现为进程或线程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。如下图:

非阻塞 I/O 模型

非阻塞式IO模型:进程或线程目的未达到时,不再一味的等着,而是直接返回。然后通过轮询的方式,不停的去问内核数据准备好没。如下图:

 

I/O 复用模型

Unix/Linux 环境下的 I/O 复用模型包含三组系统调用,分别是 select、poll 和 epoll(FreeBSD 中则为 kqueue)。select 出现的时间最早,在 BSD 4.2中被引入。poll 则是在 AT&T System V UNIX 版本中被引入(详情请参考 UNIX man-page)。epoll 出现在 Linux kernel 2.5.44 版本中,与之对应的 kqueue 调用则出现在 FreeBSD 4.1,早于 epoll。select 和 poll 出现的时间比较早,在当时也是比较先进的 I/O 模型了,满足了当时的需求。不过随着因特网用户的增长,C10K 问题出现。select 和 poll 已经不能满足需求了,研发更加高效的 I/O 模型迫在眉睫。到了 2000 年,FreeBSD 率先发布了 select、poll 的改进版 kqueue。Linux 平台则在 2002 年 2.5.44 中发布了 epoll。

select模式:select 有三个文件描述符集(readfds),分别是可读文件描述符集(writefds)、可写文件描述符集和异常文件描述符集(exceptfds)。应用程序可将某个 socket (文件描述符)设置到感兴趣的文件描述符集中,并调用 select 等待所感兴趣的事件发生。比如某个 socket 处于可读状态了,此时应用进程就可调用 recvfrom 函数把数据从内核空间拷贝到进程空间内,无需再等待内核准备数据了。如下图:

一般情况下,应用进程会将多个 socket 设置到感兴趣的文件描述符集中,并调用 select 等待所关注的事件(比如可读、可写)处于就绪状态。当某些 socket 处于就绪状态后,select 返回处于就绪状态的 sockct 数量。注意这里返回的是 socket 的数量,并不是具体的 socket。应用程序需要自己去确定哪些 socket 处于就绪状态了,确定之后即可进行后续操作。

 信号驱动式 I/O 模型

信号驱动式 I/O 模型是指,应用进程告诉内核,如果某个 socket 的某个事件发生时,请向我发一个信号。在收到信号后,信号对应的处理函数会进行后续处理。如下图:

异步 I/O 模型

异步 I/O 是指应用进程把文件描述符传给内核后,啥都不管了,完全由内核去操作这个文件描述符。内核完成相关操作后,会发信号告诉应用进程,某某 I/O 操作我完成了,你现在可以进行后续操作了。如下图:

上图通过 aio_read 把文件描述符、数据缓存空间,以及信号告诉内核,当文件描述符处于可读状态时,内核会亲自将数据从内核空间拷贝到应用进程指定的缓存空间呢。拷贝完在告诉进程 I/O 操作结束,就可以直接使用数据了。

几种IO模型的对比图:

对于java的IO(BIO NIO AIO)而言,以socket.read()为例子:

传统的BIO里面socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据,返回读到的数据。

对于NIO,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。

最新的AIO(Async I/O)里面会更进一步:不但等待就绪是非阻塞的,就连数据从网卡到内存的过程也是异步的。

换句话说,BIO里用户最关心“我要读”,NIO里用户最关心"我可以读了",在AIO模型里用户更需要关注的是“读完了”。

NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。

 

### Java IO模型解析 Java提供了多种输入/输出(IO)处理方式来满足不同的需求。主要分为字节流和字符流两大类[^1]。 #### 字节流 字节流用于读取或写入原始二进制数据,适用于任何类型的文件操作。InputStream及其子类负责从源读取字节数组;OutputStream及其派生类则用来向目的地写出字节序列。常见的实现有FileInputStream, FileOutputStream等。 #### 字符流 当涉及到文本文件或其他基于Unicode编码的数据传输时,则更倾向于使用Reader/Writer家族成员来进行高效而便捷的操作。它们可以自动完成字符集转换工作,在内部会调用相应的底层字节流对象执行实际I/O动作。BufferedReader 和 BufferedWriter 是两个非常实用的例子,前者支持按行读取字符串并带有缓冲机制提高效率,后者允许一次性提交大量文本内容到目标位置同时也能提供缓存功能加快速度。 除了上述两种基本形式外,还有其他一些重要的概念和技术: - **NIO(New Input Output)**: 自JDK 1.4引入的新一代API集合,它不仅改进了传统阻塞模式下的性能表现,还增加了非阻塞特性以及内存映射文件的支持等功能。 - **Channels & Buffers**: NIO中最重要的组件就是通道(Channel)与缓冲区(Buffer),通过Channel可以直接访问操作系统级别的资源描述符,并利用Buffer作为中介容器传递信息给应用程序层面上的对象实例化过程之中。 ```java // 创建一个新的文件示例 import java.io.File; import java.io.IOException; public class CreateNewFileExample { public static void main(String[] args) { try { File myObj = new File("filename.txt"); if (myObj.createNewFile()) { System.out.println("File created: " + myObj.getName()); } else { System.out.println("File already exists."); } } catch (IOException e) { System.out.println("An error occurred."); e.printStackTrace(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值