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 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你 ...
随机推荐
- PhotoShop常用的功能汇总
1.将图层变为"智能对象"后如何调整大小? 答: ctrl + T 2.如何对文字添加投影? 答: 点击文字图层,“图层”->"图层样式"->&qu ...
- printf()、sprintf()、vprintf()、vsprintf()(转)
转自http://sumsung753.blog.163.com/blog/static/14636450120112151092934/ 一.printf() printf()函数优点在于可以格式化 ...
- Firebug: 已拦截跨源请求:同源策略禁止读取位于XXX的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-
第一种,就是在被请求的程序中添加HTTP头,即CORS跨域(跨域资源共享,Cross-Origin Resource Sharing) 如: Response.Headers.Add("Ac ...
- MapServer Tutorial——MapServer7.2.1教程学习——第一节用例实践:Example1.5 Adding a raster layer
MapServer Tutorial——MapServer7.2.1教程学习——第一节用例实践:Example1.5 Adding a raster layer 一.前言 MapServer不仅支持 ...
- spyder中让生成的图像单独在窗口中显示
IPython 支持两种形式的绘图 终端输出图像新窗口输出图像方式 1 能够非常方便的保存输出记录(如将`IPython 终端输出转换成 Html 文件) 方式 2 则可以交互式的放大.拖动图片,并且 ...
- 对Java中properties类的理解
转载于:https://www.cnblogs.com/bakari/p/3562244.html 一.Java Properties类 Java中有个比较重要的类Properties(Java.ut ...
- python之路-----前端之http协议
一.概述 HTTP(hypertext transport protocol),即超文本传输协议.这个协议详细规定了浏览器和万维网服务器之间互相通信的规则(B/S架构). HTTP就是一个基于TCP的 ...
- Python_Mix*生成器,生成器函数,推导式,生成器表达式
生成器: 生成器的本质就是迭代器 生成器一般由生成器函数或者生成器表达式来创建,其实就是手写的迭代器 def func(): print('abc') yield 222 #由于函数中有了yield ...
- UML介绍
UML是什么 Unified Modeling Language (UML)又称统一建模语言或标准建模语言,是始于1997年一个OMG标准,它是一个支持模型化和软件系统开发的图形化语言,为软件开发的所 ...
- Django部署以及整合celery
前言 Djngo部署的结构一般都是nginx+uwsgi+python web 一.新建一个Djang项目并合并celery 项目名随便打的..命名规范驼峰啥的别和我扯犊子哈 跑一下,然后我们就有一个 ...