I/O 模型与 Java
本文主要记录 Java 中 I/O 模型及相关知识点 。另,由于自身知识水平有限,如有不正之处,望各位能够谅解,欢迎批评指正!
一、I/O 基础
由于 Java 中 I/O 相关的 API ,无论是 BIO 还是 NIO,都是相对抽象的。所以我先整理一下 I/O 的基础知识,了解一下操作系统是如何处理 I/O 操作的。这一部分的内容,主要来自于『Java NIO』一书,中译版的 1.4 节:I/O 概念。
1.1 缓冲区操作
缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。I/O 操作也就是 input/output,输入/输出操作,实际上就是将数据移进/移出缓冲区。进程执行 I/O 操作,归结起来,也就是向操作系统发出请求,让它将缓冲区排干(写),或者用数据将缓冲区填满(读)。这里面的读可能好理解一点,写估计就有点不太直白了。个人理解:就是将缓冲区之前写入的数据移出去,也就是发送,感觉和 Java 中 I/O 操作的 flush() 方法有点类似的感觉。
下图简单描述了数据从外部磁盘向运行中的进程的内存区域移动的过程。进程使用 read() 系统调用,要求其缓冲区被填满。内核随即向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,当磁盘控 制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进程执行 read() 调用时指定的缓冲区。

注意,图中用户空间和内核空间的概念。用户空间是常规进程所在区域,是非特权区域:比如,在该区域执行的代码不能直接访问硬件设备。内核空间是操作系统所在区域,是特权区域:它能与设备控制器通讯,控制着用户区域 进程的运行状态,等等。最重要的是,所有 I/O 都直接或间接通过内核空间。
为什么 I/O 操作都要直接或间接的经过内核空间?我能想到的有两点:一是安全,二是效率。书中也介绍了分散/汇聚高效的处理数据原因。
二、BIO 和 NIO
在前面演示的 I/O 过程中,大致分为两个步骤:
- 用户空间的进程发起 I/O 请求,操作系统接收到请求后,系统内核将数据从硬件中读取到内核缓冲区(准备需要的数据);
- 内核空间的系统内核将数据从内核缓冲区移动到用户空间的缓冲区,然后数据可供用户进程使用(填充缓冲区)。
显然,这两个步骤或多或少都会消耗一定的时间。那么 BIO 和 NIO 的区别就在这两个步骤中区别开了。
2.1 BIO
IO 称之为阻塞 I/O。在第一个阶段,用户进程发起 I/O 请求后,程序会处于阻塞状态,直到内核将数据准备好。然后,在第二个阶段,用户进程仍然处于阻塞状态,直到内核完成用户空间缓冲区的填充,最后用户进程开始使用数据。也就是说用户进程从发起请求的那一刻,直到数据可用,一直都处于阻塞状态。
2.2 NIO
NIO 称之为非阻塞 I/O。在第一个阶段,用户进程发起 I/O 请求后,如果内核未将数据准备好,请求会直接返回,并表明数据没有准备好。此时用户进程不会继续等待,会继续运行。一般我们都是重复的去检查内核是否将数据准备好。当数据准备好之后,进入第二阶段,发起填充缓冲区的请求,此时即便是 NIO 也会阻塞到用户空间的缓冲区填充完成。
由此,我们可以发现,BIO 和 NIO 的区别是在 I/O 操作的第一个阶段,而第二个阶段,两者都会阻塞。
在这里,我曾经有过这样的误会:既然 NIO 也是循环的去检查内核是否将数据准备好,程序本身就是要去处理即将获取的数据,那么它非阻塞的意义在哪儿呢,还不如直接阻塞的等待,何必浪费 CPU 不停的轮询呢?紧接着我看了多路复用 I/O,才算明白了 NIO 的优势。
三、多路复用 I/O
实际上 Java 中的 NIO 是多路复用 I/O 模型,真正的优势是其线程模型。而实现这个线程模型的关键在于其使用的系统调用是 select()。在介绍 select()调用前,先声明一点:Java 中的 NIO 非阻塞的特性仅适用于网络 I/O,所以下面主要针对网络 I/O 来讲。
select() 会返回当前所有数据准备就绪的 Socket,如果当前没有数据准备就绪的 Socket,select() 就会阻塞。那么只需要一个线程就能处理多个 Socket 的数据。回顾一下 BIO,当我们建立一个 Socket 连接时,这个建立连接的线程就会阻塞,直到有数据可用,也就是一个线程必须对应一个 Socket。
BIO 的劣势在于单机中线程数量是有限的,我觉得肯定比 Socket 连接数少很多吧。其次当线程数较多时,线程之间的切换会消耗掉很多的系统资源。并且当大部分的 Socket 连接的数据传输并不是很密集的时候,这些 Socket 对应的线程都处于阻塞状态,这显然是空占内存!此时就是使用 Java NIO 的绝佳机会。
可能说了那么多,大家都觉得 BIO 一无是处,Java 网络编程直接上 NIO 就 Okay!其实我觉得两者都有自己适用的环境,没有最好的,只有更合适的。
- BIO 适用场景:
普通的文件读写、Socket 连接数量较少、数据传输密集等场景。在网络编程中,一般用于客户端。 - NIO 适用场景:
普通的文件读写、Socket 连接数较多、需要考虑高并发等场景。在网络编程中,一般用于服务端。
四、AIO
AIO 称之为异步 I/O。AIO 是最理想的 I/O 处理方式。在 I/O 操作的两个阶段均不阻塞,当用户程序发起 I/O 请求时直接返回。然后系统内核完成接下的所有操作之后,会通知用户程序直接使用数据。
由于第二个阶段是由系统内核主动完成的,所以 AIO 需要操作系统底层的支持。
五、总结
下面是我在网上扒来的一张图,很好的说明了各类 I/O 模型。

关于同步与异步、阻塞与非阻塞,文末给出的参考资料值得一看。
参考资料:
- 网络IO模型 - leung - 开源中国 文中的流程图非常好,有助于理解
- [思考] 也谈同步异步I/O - SmithFox
- Java NIO:浅析I/O模型 - 海 子 - 博客园
- 怎样理解阻塞非阻塞与同步异步的区别? - 知乎 参考排名较高的回答
- IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇) - 智障大师 的专栏 - CSDN博客 文末的比喻很形象
I/O 模型与 Java的更多相关文章
- Java内存模型与Java线程实现原理
硬件的效率与一致性 基于高速缓存的存储交互很好的解决了处理器和内存的速度矛盾,但是也为计算机系统带来了更高的复杂度,因为引入了一个新问题:缓存一致性. 在多处理器系统中,每个处理器都有自己的高速缓存, ...
- 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型
原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...
- 【转】JVM内存结构 VS Java内存模型 VS Java对象模型
JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途. 其中有些区域随着虚拟机进程的启动而 ...
- 并发研究之Java内存模型(Java Memory Model)
Java内存模型JMM java内存模型定义 上一遍文章我们讲到了CPU缓存一致性以及内存屏障问题.那么Java作为一个跨平台的语言,它的实现要面对不同的底层硬件系统,设计一个中间层模型来屏蔽底层的硬 ...
- java内存结构JVM——java内存模型JMM——java对象模型JOM
JVM内存结构 Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途.其中有些区域随着虚拟机进程的启动而存在,而有些区 ...
- Java网络编程和NIO详解3:IO模型与Java网络编程模型
Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...
- JAVA内存模型(Java Memory Model ,JMM)
http://blog.csdn.net/hxpjava1/article/details/55189077 JVM有主内存(Main Memory)和工作内存(Working Memory),主内存 ...
- java内存模型(Java Memory Model)
内容导航: Java内存模型 硬件存储体系结构 Java内存模型和硬件存储体系之间的桥梁: 共享对象的可见性 竞争条件 Java内存模型规定了JVM怎样与计算机存储系统(RAM)协调工作.JVM是一个 ...
- JVM内存结构 VS Java内存模型 VS Java对象模型
前面几篇文章中, 系统的学习了下JVM内存结构.Java内存模型.Java对象模型, 但是发现自己还是对这三者的概念和区别比较模糊, 傻傻分不清楚.所以就有了这篇文章, 本文主要是对这三个技术点再做一 ...
- 硬件内存模型到 Java 内存模型,这些硬核知识你知多少?
Java 内存模型跟上一篇 JVM 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你 ...
随机推荐
- js删除数组中元素 delete 和splice的区别
例如我有一个数组: var array = ["aa","dd","cc","aa"] ,我想删除这个数组的“dd”元素 ...
- 【C语言】数组知识点总结
[C语言]数组知识点总结 标签: 数组 2018年04月12日 17:44:4481人阅读 评论(0) 收藏 举报 分类: C语言知识总结(4) 版权声明:本文为博主原创文章,未经博主允许不得转载 ...
- onceAgain, 这是一个py群的群公告说明
群规: 1. 不骚扰人 2. 不涉及娱乐政治 3. 主要就这两条 入门参考:https://book.douban.com/review/9547077/ qq群/网盘:523445644 # 加群 ...
- 《Python量化交易教程》第一部分新手入门 第1天:谁来给我讲讲Python?
一.量化投资视频学习课程 二.Python手把手教学 第1天:谁来给我讲讲Python? PS: 1.注意使用方法,这个以后都有大用 2.注意符号的使用方式 3.尽量用英文表达 4.本日学习内容以及其 ...
- flask基础---第三篇
flask中request的一些方法 首先from flask import request 1.request.path 2.request.host 3.request.host_url from ...
- 解决MyEclipse启动慢,使用卡顿问题
卡顿原因: 1.启动的服务和插件过多,导致启动和运行缓慢,电脑配置较差的直接会卡死没有响应 2.软件运行内存设置不足,导致没有足够的空间运行软件,致使软件卡顿 解决方法: windows --> ...
- python之路-----前端之js(一)
一.JS发展历史 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名ScriptEase.(客户端执行的语言) Netscape ...
- Python_Mix*匿名函数,sorted,filter,map,递归函数,二分法查找
lambda匿名函数(函数名统一都叫lambda) 为了解决简单的需求而设计的一句话函数 语法: lambda 参数 返回值 n = lambda a,b: max(a,b) ret = n(9,4) ...
- 简易祖玛--canvas
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- windows下启动和运行分布式消息中间件消息队列 kafka
本文转载至:https://www.cnblogs.com/flower1990/p/7466882.html 一.安装JAVA JDK 1.下载安装包 http://www.oracle.com/t ...