少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别
1、引言
很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO?
在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之间的差异、典型用例,以及这些差异如何影响我们的网络编程或数据传输代码的设计和实现的。
本文没有复杂理论,也没有像网上基它文章一样千篇一律的复制粘贴,有的只是接地气的通俗易懂,希望能给你带来帮助。
(本文同步发布于:http://www.52im.net/thread-2635-1-1.html)
2、相关文章
3、Java NIO和IO的主要区别
下表总结了Java NIO和IO之间的主要区别。我将在表格后面的部分中详细介绍每个区别。
3.1 Stream Oriented vs. Buffer Oriented
Java NIO和IO之间的第一个重要区别是IO是面向流的,其中NIO是面向缓冲区的。那么,这意味着什么?
面向流的Java IO意味着您可以从流中一次读取一个或多个字节。你对读取的字节做什么取决于你。它们不会缓存在任何地方。此外,您无法在流中的数据中前后移动。如果需要在从流中读取的数据中前后移动,则需要先将其缓存在缓冲区中。
Java NIO的面向缓冲区的方法略有不同。数据被读入缓冲区,稍后处理该缓冲区。你可以根据需要在缓冲区中前后移动。这使你在处理过程中具有更大的灵活性。但是,你还需要检查缓冲区是否包含完整处理所需的所有数据。并且,你需要确保在将更多数据读入缓冲区时,不要覆盖尚未处理的缓冲区中的数据。
3.2 Blocking vs. Non-blocking IO
Java IO的各种流都是blocking的。这意味着,当线程调用read()或write()时,该线程将被阻塞,直到有一些数据要读取,或者数据被完全写入,在此期间,该线程无法执行任何其他操作。
Java NIO的非阻塞模式允许线程请求从通道读取数据,并且只获取当前可用的内容,或者根本没有数据,如果当前没有数据可用。线程可以继续使用其他内容,而不是在数据可供读取之前保持阻塞状态。
非阻塞写入也是如此,线程可以请求将某些数据写入通道,但不要等待它完全写入。然后线程可以继续并在同一时间做其他事情。
线程在IO调用中没有阻塞时花费空闲时间,通常在此期间在其他通道上执行IO。也就是说,单个线程现在可以管理多个输入和输出通道。
4、Selectors
Java NIO的选择器允许单个线程监视多个输入通道。你可以使用选择器注册多个通道,然后使用单个线程“选择”具有可用于处理的输入的通道,或者选择准备写入的通道。这种选择器机制使单个线程可以轻松管理多个通道。
5、NIO和经典IO如何影响应用程序的设计?
选择NIO或IO作为IO工具包可能会影响应用程序设计的以下方面:
1)API调用NIO或IO类;
2)处理数据;
3)用于处理数据的线程数。
5.1 API调用
当然,使用NIO时的API调用看起来与使用IO时不同。这并不奇怪。而不是仅仅从例如InputStream读取字节的数据字节,必须首先将数据读入缓冲区,然后从那里进行处理。
5.2 数据处理
使用纯NIO设计与IO设计时,数据处理也会受到影响。
在IO设计中,您从InputStream或Reader中读取字节的数据字节。想象一下,您正在处理基于行的文本数据流。
例如:
Name: Anna
Age: 25
Email: [url=mailto:anna@mailserver.com]anna@mailserver.com[/url]
Phone: 1234567890
这个文本行流可以像这样处理:
InputStream input = ... ; // get the InputStream from the client socket
BufferedReader reader = newBufferedReader(newInputStreamReader(input));
String nameLine = reader.readLine();
String ageLine = reader.readLine();
String emailLine = reader.readLine();
String phoneLine = reader.readLine();
注意处理状态是如何,由程序执行的程度决定的。换句话说,一旦第一个reader.readLine()方法返回,您就确定已经读取了整行文本。readLine()会阻塞直到读取整行,这就是原因。您还知道此行包含名称。同样,当第二个readLine()调用返回时,您知道此行包含年龄等。
正如您所看到的,只有当有新数据要读取时,程序才会进行,并且对于每个步骤,您都知道该数据是什么。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动(通常不会)。
此图中还说明了此原则:
▲ Java IO:从阻塞流中读取数据
NIO的实现看起来会有所不同,这是一个简化的例子:
ByteBuffer buffer = ByteBuffer.allocate(48);
intbytesRead = inChannel.read(buffer);
注意第二行从通道读取字节到ByteBuffer。当该方法调用返回时,您不知道所需的所有数据是否都在缓冲区内。你只知道缓冲区包含一些字节,这使得处理更加困难。
想象一下,在第一次读取(缓冲)调用之后,是否所有读入缓冲区的内容都是半行。例如,“姓名:An”。你能处理这些数据吗?并不是的。在完成任何数据的处理之前,您需要等待至少一整行数据进入缓冲区。
那么你怎么知道缓冲区是否包含足够的数据来处理它?好吧,你没有。找出的唯一方法是查看缓冲区中的数据。结果是,在您知道所有数据是否存在之前,您可能需要多次检查缓冲区中的数据。这既低效又可能在程序设计方面变得混乱。
例如:
ByteBuffer buffer = ByteBuffer.allocate(48);
intbytesRead = inChannel.read(buffer);
while(! bufferFull(bytesRead) ) {
bytesRead = inChannel.read(buffer);
}
bufferFull()方法必须跟踪读入缓冲区的数据量,并返回true或false,具体取决于缓冲区是否已满。换句话说,如果缓冲区已准备好进行处理,则认为它已满。
bufferFull()方法扫描缓冲区,但必须使缓冲区保持与调用bufferFull()方法之前相同的状态。如果不是,则可能无法在正确的位置读入读入缓冲区的下一个数据。这不是不可能的,但这是另一个需要注意的问题。
如果缓冲区已满,则可以对其进行处理。如果它不满,您可能能够部分处理那里的任何数据,如果这在您的特定情况下是有意义的。在许多情况下,它没有。
这个图中说明了is-data-in-buffer-ready循环:
▲ Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中
6、什么时候该用NIO?什么时候该用经典IO?
NIO允许您仅使用一个(或几个)线程来管理多个通道(网络连接或文件),但成本是解析数据可能比从阻塞流中读取数据时更复杂。
如果您需要同时管理数千个打开的连接,每个只发送一些数据,例如聊天服务器,在NIO中实现服务器可能是一个优势。同样,如果您需要与其他计算机保持大量开放连接,例如在P2P网络中,使用单个线程来管理所有出站连接可能是一个优势。
此图中说明了这一个线程,多个连接设计:
▲ Java NIO:管理多个连接的单个线程
如果您拥有较少带宽的连接,一次发送大量数据,那么可能最经典的IO服务器实现可能是最合适的。
此图说明了经典的IO服务器设计:
▲ Java IO:经典的IO服务器设计 - 由一个线程处理的一个连接
7、更简化的理解
以众所周之的数据读取过程为例,我们来一个更简化的理解。
对于数据读取,就读取速度来说:CPU > 内存 > 硬盘。
I- 就是从硬盘到内存
O- 就是从内存到硬盘
第一种方式:从硬盘读取数据,然后程序一直等,数据读完后,继续你的操作。这种方式是最简单的,叫阻塞IO(也就是经典IO)。
第二种方式:从硬盘读取数据,然后程序继续向下执行,等数据读取完后,通知当前程序读取完成(对硬件来说叫中断,对程序来说叫回调),然后此程序可以立即处理读取的数据,也可以执行完当前操作后再对读取完的数据进行操作。
8、总而言之
还是以数据读取为例,操作系统是按块Block(块)从硬盘拿数据,就如同一个大脸盆,一下子就放入了一盆水。但是,当 Java 使用的时候,旧的 IO(经典IO)确实基于 流 Stream的,也就是虽然操作系统给我了一脸盆水,但是我得用吸管慢慢喝。
由于经典IO的重重落后理念,于是,NIO 横空出世。。。
附录:更多NIO异步网络编程资料
《Java新一代网络编程模型AIO原理及Linux系统AIO介绍》
《开源NIO框架八卦——到底是先有MINA还是先有Netty?》
《NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示》
《NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示》
《NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战》
《NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战》
《Netty 4.x学习(二):Channel和Pipeline详解》
《Apache Mina框架高级篇(一):IoFilter详解》
《Apache Mina框架高级篇(二):IoHandler详解》
《Apache MINA2.0 开发指南(中文版)[附件下载]》
《实践总结:Netty3.x升级Netty4.x遇到的那些坑(线程篇)》
《实践总结:Netty3.x VS Netty4.x的线程模型》
《Twitter:如何使用Netty 4来减少JVM的GC开销(译文)》
《Netty干货分享:京东京麦的生产级TCP网关技术实践总结》
《新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析》
《写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略》
>> 更多同类文章 ……
(本文同步发布于:http://www.52im.net/thread-2635-1-1.html)
少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别的更多相关文章
- 十分钟带你读懂《增长黑客》zz
背景 “If you are not growing, then you are dying. ”(如果企业不在增长,那么就是在衰亡!) 这句话适用于企业,也适用于个人.人生毕竟不像企业,是非成败,似 ...
- 黑马程序员:3分钟带你读懂C/C++学习路线
随着互联网及互联网+深入蓬勃的发展,经过40余年的时间洗礼,C/C++俨然已成为一门贵族语言,出色的性能使之成为高级语言中的性能王者.而在今天,它又扮演着什么样重要的角色呢?请往下看: 后端服务器,移 ...
- 五分钟带你读懂 TCP全连接队列(图文并茂)
爱生活,爱编码,微信搜一搜[架构技术专栏]关注这个喜欢分享的地方. 本文 架构技术专栏 已收录,有各种视频.资料以及技术文章. 一.问题 今天有个小伙伴跑过来告诉我有个奇怪的问题需要协助下,问题确实也 ...
- 五分钟带你读懂 堆 —— heap(内含JavaScript代码实现!!)
一.概念 说起堆,我们就想起了土堆,把土堆起来,当我们要用土的时候,首先用到最上面的土.类似地,堆其实是一种优先队列,按照某种优先级将数字"堆"起来,每次取得时候从堆顶取. 堆 ...
- 【干货!!】十分钟带你搞懂 Java AQS 核心设计与实现!!!
前言 这篇文章写完放着也蛮久的了,今天终于发布了,对于拖延症患者来说也真是不容易-哈哈哈. 言归正传,其实吧..我觉得对于大部分想了解 AQS 的朋友来说,明白 AQS 是个啥玩意儿以及为啥需要 AQ ...
- 实战 | 一文带你读懂Nginx反向代理
一个执着于技术的公众号 前言 在前面的章节中,我们已经学习了nginx基础知识: 给小白的 Nginx 10分钟入门指南 Nginx编译安装及常用命令 完全卸载nginx的详细步骤 Nginx 配置文 ...
- 一文带你读懂什么是vxlan网络
一个执着于技术的公众号 一.背景 随着云计算.虚拟化相关技术的发展,传统网络无法满足大规模.灵活性要求高的云数据中心的要求,于是便有了overlay网络的概念.overlay网络中被广泛应用的就是vx ...
- 五分钟让你读懂UML常见类图
相信各位同学在阅读一些源码分析类文章或是设计应用架构时没少与UML类图打交道.实际上,UML类图中最常用到的元素五分钟就能掌握,经常看到UML类图但还不太熟悉的小伙伴赶紧来一起认识一下它吧:) 一 ...
- 【 全干货 】5 分钟带你看懂 Docker !
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者丨唐文广:腾讯工程师,负责无线研发部地图测试. 导语:Docker,近两年才流行起来的超轻量级虚拟机,它可以让你轻松完成持续集成.自动交付 ...
随机推荐
- IE6\7\8 :last-child 和 :first-chlid 兼容
IE9以下不支持last-child ,只支持first-child,边框尽量用上边框.
- ROUND function and arithmetic overflow
遇到如下错误 Arithmetic overflow error converting expression to data type numeric. ), ); https://stackover ...
- POJ 2629:Common permutation
Common permutation Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5510 Accepted: 168 ...
- 红米note怎么打开USB调试模式
红米note到货后,打开USB调试模式是一些新手很棘手的问题,所以要手机助手成功识别红米note,你必须打开红米note的USB调试模式.在安卓4.2版本中,系统的USB调试模式不是非常简单地被打开的 ...
- ANT-普通替换和正则替换
ant提供了两个指令用于编译时修改文件,好处就不说了 ,就说说如何使用吧. replaceregexp 和 replace的区别就和java中String replace和replaceAll一样 , ...
- 4.7.3 Canonical LR(1) Parsing Tables
4.7.3 Canonical LR(1) Parsing Tables We now give the rules for constructing the LR(1) ACTION and GOT ...
- HTTP请求 响应状态码
1.请求响应方式 get是从服务器上获取数据,post是向服务器传送数据.get 数据在地址栏中明文的形式发送, post则不是,传递的数据比get多 据RFC2616标准(现行的HT ...
- bzoj 1044: [HAOI2008]木棍分割【二分+dp】
对于第一问二分然后贪心判断即可 对于第二问,设f[i][j]为已经到j为止砍了i段,转移的话从$$ f[i][j]=\sigema f[k][j-1] (s[j]-s[k-1]<=ans) 这里 ...
- bzoj 1753: [Usaco2005 qua]Who's in the Middle【排序】
--这可能是早年Pascal盛行的时候考排序的吧居然还是Glod-- #include<iostream> #include<cstdio> #include<algor ...
- weiphp插件开发注意
插件命名要规范,插件名文件名,控制器名,模型名要以大写开头.不然的话会有惊喜!╮(╯▽╰)╭