IO 和 NIO 的思考
输入输出是操作系统不可或缺的一部分,大致分为两类:面向磁盘和面向网络。在 Java 中有3种 I/O 类型:BIO、NIO 和 AIO,分别是同步阻塞、同步非阻塞和异步非阻塞 I/O,这里着重描述 BIO 和 NIO 的区别和常用的编程模型。
1. 为什么设计 NIO
一个直接原因就是为了更好的利用操作系统特性,改善和扩展原有 API。与 NIO 相关的规范有两个:
- JSR 51:它是 NIO 的第一个规范,关注缓冲区、通道和字符集的设计,引入一个简单的面向缓冲区的 I/O 模型,并且提供一套非阻塞、I/O 多路复用、可扩展的 API
- JSR 203(NIO.2):它在前者的基础上,添加新的文件系统的抽象,完善现有 Socket 通道的配置,添加多播数据报的支持,并且定义了一个异步 I/O 编程 API
那么,传统的 BIO 又有什么弊端?NIO 又是如何改进的?可以从两方面进行说明。
1.2 文件操作
关于 java.io.file,它的不足之处在于:
- 查询文件属性时,如修改时间或文件类型,都会发生系统调用,并且这些组合操作非常常见,造成性能问题
- 部分方法在发生错误时返回 false 而不是抛出异常,比如 delete、rename,不知操作失败的原因
- 一些 OS 高级功能不支持,比如符号链接、文件锁定、内存映射等
而 NIO 支持批量获取文件属性,对文件、目录的处理也重新设计,提供 FileLock、MappedByteBuffer 等支持 OS 高级功能。
1.3 网络通信
BIO 是同步阻塞、基于流的 I/O,阻塞就意味着当 Socket 输入流中无数据可读取时,调用线程挂起,直到有数据读取,期间不能处理其他请求,如果来了一个新连接,就只能再新建一个线程处理。
随着连接数的增加,BIO 将会创建大量线程,而一个计算机能打开的进程数或线程数是有限的,严重的时候可能会导致应用崩溃无响应。一个有效的解决办法是使用线程池,限制最大线程数,但它同时也限制了最大连接数。
NIO 将读写改为非阻塞,无数据可读,线程返回线程池,可用于处理其他连接。它对原始 I/O 提供了新的抽象 - Channel(通道),并且提供基于缓冲区的读写 API。Channel 表示一个到硬件设备、文件或网络套接字的连接,与 java.net.Socket 的区别是:
- 可配置非阻塞,允许事件驱动的设计,提供了一种更加可扩展的服务器开发
- 面向缓冲区,可实现零拷贝执行 I/O ,只不过有一端必须是 FileChannel
相同环境下,BIO 的线程全程只处理一条连接,而 NIO 的线程可处理多个连接,提高了系统的吞吐能力。NIO 在服务器进行纵向扩展(比如增加内存、CPU)或者横向扩展(比如增加服务器)往往能够比 BIO 带来更高的处理能力,使服务器具有更强的可扩展性和可伸缩性。
1.4 零拷贝
NIO 还有一个零拷贝的概念,零拷贝是指 CPU 不执行将数据从一个存储区复制到另一个存储区的操作。OS 级别的零拷贝指的是将数据发送到硬件驱动程序(网卡或磁盘驱动器)时避免从一个位置复制到另一个位置(一般是从用户空间到内核空间),反之亦然。NIO 中的零拷贝就是这样,只不过它只针对在网络上发送文件。
2. I/O 模型的选择
一般的,我们潜意识的会认为 NIO 比 BIO 的性能高,其实不尽然,当然了有个读取方式的问题,read(byte[]) 和 read(ByteBuffer)应该没区别吧?所以如果系统的并发量不高,两个用谁都行。
BIO 的问题通常会在海量的连接下体现出来,由于它不能充分利用、压榨一台服务器的性能,不管怎么扩展,它能处理的连接数与机器性能往往是非线性的,付出和收获不成正比。如果你的应用面临的连接不断增加,特别是存在大量的长连接,此时就要选择 NIO,它不仅提高了单机处理能力,还能节省服务器成本。
NIO 相比 BIO 的重点在于可扩展性,在选择 I/O 模型时,需要结合业务场景,综合考虑以下几点:
- 预计最大的并发数
- 短连接还是长连接
- 预计每个连接的数据量,即流量的大小
- NIO 灵活,但代价是编程复杂
3. 编程模型
BIO 的编程模型是一连接一处理线程,采用线程池优化。
NIO 典型的编程模型是 Reactor,事件复用器通知套接字何时准备好读取和写入操作的事件,将事件传递给合适的处理程序,由该程序负责实际的读取或写入。对于读操作基本过程如下:
- 处理程序声明感兴趣的 I/O 事件 - 读取事件
- 事件复用器等待事件
- 一个事件发生,复用器被唤醒并调用适当的处理程序
- 处理程序执行实际的读取操作,处理读取的数据,重新声明关注的 I/O 事件,并将控制权返回给调度程序
与 Reactor 相对的还有一个 AIO 的 Proactor 模型,它是异步 I/O,事件复用器等待 I/O 操作完成的事件,它是真正的异步,因为实际的 I/O 操作完全由操作系统执行。对于读操作,它的做法是:
- 处理程序启动异步读取操作,在这种情况下,处理程序不关心 I/O 就绪事件,而是关注接收完成的事件
- 事件复用器等待操作完成
- 当事件复用器等待时,OS 并行的在内核线程中执行读操作,将数据放入用户定义的缓冲区,读取完成后通知事件多路复用器
- 事件复用器调用适当的处理程序
- 处理程序处理用户定义缓冲区的数据,启动新的异步操作,并将控制返回给事件多路复用器
4. 小结
I/O 模型经常对比的是阻塞和非阻塞,还有同步和异步,在《UNIX 网络编程》第6章已经给出了它们的区别,并且给出的图示很直观。这里简单做下说明,以 UDP 为例,读取一个数据包可以分为两个阶段:
- 第一阶段:应用进程发起系统调用,内核无数据包准备好,等待数据
- 第二阶段:数据包准备好,OS 将数据从内核复制到用户空间
阻塞和非阻塞描述的是第一阶段无数据可读取时线程是否挂起;同步和异步描述的是第二阶段,在数据复制过程中线程是否参与和挂起。注意 NIO/BIO 都是同步 I/O,NIO 对应 UNP 中描述的 I/O 复用模型。
IO 和 NIO 的思考的更多相关文章
- 面试题思考:IO 和 NIO的区别,NIO优点
面试时答: IO是面向流的,NIO是面向缓冲区的 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方: NIO则能前后移动流中的数据,因为是面向缓冲区的 ...
- java面试题之----IO与NIO的区别
JAVA NIO vs IO 当我们学习了Java NIO和IO后,我们很快就会思考一个问题: 什么时候应该使用IO,什么时候我应该使用NIO 在下文中我会尝试用例子阐述java NIO 和IO的区别 ...
- IO、NIO实现简单聊天室,附带问题解析
本篇文章主要使用IO和NIO的形式来实现一个简单的聊天室,并且说明IO方法存在的问题,而NIO又是如何解决的. 大概的框架为,先提供思路和大概框架图--代码--问题及解决方式,这样会容易看一点 ...
- java的nio之:java的nio系列教程之java的io和nio的区别
当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异.它们的使用场景,以及它们如何影响您的代 ...
- 面试题_66_to_75_Java IO 和 NIO 的面试题
IO 是 Java 面试中一个非常重要的点.你应该很好掌握 Java IO,NIO,NIO2 以及与操作系统,磁盘 IO 相关的基础知识.下面是 Java IO 中经常问的问题. 66)在我 Java ...
- 传统IO与NIO的比较
本文并非Java.io或Java.nio的使用手册,也不是如何使用Java.io与Java.nio的技术文档.这里只是尝试比较这两个包,用最简单的方式突出它们的区别和各自的特性.Java.nio提出了 ...
- Java IO 和 NIO
昨天面试问到了有关Java NIO的问题,没有答上来.于是,在网上看到了一篇很有用的系列文章讲Java IO的,浅显易懂.后面的备注里有该系列文章的链接.内容不算很长,需要两个小时肯定看完了,将该系列 ...
- JAVA中IO和NIO的详解分析,内容来自网络和自己总结
用一个例子来阐释: 一辆客车上有10个乘客,他们的目的地各不相同,当没有售票员的时候,司机就需要不断的询问每一站是否有乘客需要下车,需要则停下,不需要则继续开车,这种就是阻塞的方式. 当有售票员的时候 ...
- Nio学习4——EchoServer在IO,NIO,NIO.2中的实现
堵塞IO实现: public class PlainEchoServer { public void serve(int port) throws IOException { final Server ...
随机推荐
- 「SDOI2018」物理实验
题目大意: 这题不好描述,直接看原题吧…… 题解: 很无脑的题……就是卡精度+难写.代码能力还是太差了. 其实可以直接用long double肝过去.但我的代码似乎太丑了,以至于跑得奇慢无比. 代码: ...
- 【BZOJ 2004】: [Hnoi2010]Bus 公交线路
题目链接: TP 题解: 所以说,超显眼的数据范围啊. 很显然我们对于每个P的区间都是要有k个站被bus停留,然后考虑转移的话应该是把这k个站里的某个bus往前走,那么转移也很显然了,n的范围很大 ...
- BZOJ_1803_Spoj1487 Query on a tree III_主席树+dfs序
BZOJ_1803_Spoj1487 Query on a tree III_主席树 Description You are given a node-labeled rooted tree with ...
- java创建线程的几种方式,了解一下
1.继承Thread,重写run() public class MyThread extends Thread{ @Override public void run() { System.out.p ...
- Charles 如何破解与连接手机进行抓包
破解charles: 由于本人工作原因,现使用的为mac笔记本,但是基本的使用原理都是一样的,以下为如何破解charles与连接手机进行抓包详解.工具如有需要着请留言. 当前下载使用的版本为:char ...
- 对抗明文口令泄露 —— Web 前端慢 Hash
(更新:https://www.cnblogs.com/index-html/p/frontend_kdf.html ) 0x00 前言 天下武功,唯快不破.但在密码学中则不同.算法越快,越容易破. ...
- 开发小白也毫无压力的hexo静态博客建站全攻略 - 躺坑后亲诉心路历程
目录 基本原理 方法1 - 本机Windows下建站 (力荐) 下载安装node.js 用管理员权限打开命令行,安装hexo-cli和hexo 下载安装git 初始化hexo 使用hexo gener ...
- 用系统为centos6的主机,搭建PXE服务器,实现批量安装centos6,7系统
1. iptables -F setenforce 0 临时关掉selinux,清掉防火墙 永久生效更改配置文件:vim /etc/sysconfig/selinux chkconfig iptabl ...
- Multi Reflection (Pro Only)
Case 14 - Double Injection in HTML Context with Double Quotes https://brutelogic.com.br/multi/double ...
- Proj.Net 投影介绍
Proj.Net是开源地图投影库Proj.4的.net版本,许多GIS开源软件的投影都直接或间接地使用Proj.4的库,Proj.4是用C语言编写..Net下的开源GIS项目NetTopologySu ...