不少初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我何时应该使用经典IO,何时应该使用NIO?html
在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之间的差别、典型用例,以及这些差别如何影响咱们的网络编程或数据传输代码的设计和实现的。程序员
本文没有复杂理论,也没有像网上基它文章同样千篇一概的复制粘贴,有的只是接地气的通俗易懂,但愿能给你带来帮助。编程
(本文同步发布于:http://www.52im.net/thread-26...)缓存
《Java新一代网络编程模型AIO原理及Linux系统AIO介绍》
《Java NIO基础视频教程、MINA视频教程、Netty快速入门视频》安全
下表总结了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。也就是说,单个线程如今能够管理多个输入和输出通道。
Java NIO的选择器容许单个线程监视多个输入通道。你可使用选择器注册多个通道,而后使用单个线程“选择”具备可用于处理的输入的通道,或者选择准备写入的通道。这种选择器机制使单个线程能够轻松管理多个通道。
选择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:从通道读取数据,直到全部须要的数据都在缓冲区中
NIO容许您仅使用一个(或几个)线程来管理多个通道(网络链接或文件),但成本是解析数据可能比从阻塞流中读取数据时更复杂。
若是您须要同时管理数千个打开的链接,每一个只发送一些数据,例如聊天服务器,在NIO中实现服务器多是一个优点。一样,若是您须要与其余计算机保持大量开放链接,例如在P2P网络中,使用单个线程来管理全部出站链接多是一个优点。
此图中说明了这一个线程,多个链接设计:
▲ Java NIO:管理多个链接的单个线程
若是您拥有较少带宽的链接,一次发送大量数据,那么可能最经典的IO服务器实现多是最合适的。
此图说明了经典的IO服务器设计:
▲ Java IO:经典的IO服务器设计 - 由一个线程处理的一个链接
以众所周之的数据读取过程为例,咱们来一个更简化的理解。
对于数据读取,就读取速度来讲:CPU > 内存 > 硬盘。
I- 就是从硬盘到内存
O- 就是从内存到硬盘
第一种方式:从硬盘读取数据,而后程序一直等,数据读完后,继续你的操做。这种方式是最简单的,叫阻塞IO(也就是经典IO)。
第二种方式:从硬盘读取数据,而后程序继续向下执行,等数据读取完后,通知当前程序读取完成(对硬件来讲叫中断,对程序来讲叫回调),而后此程序能够当即处理读取的数据,也能够执行完当前操做后再对读取完的数据进行操做。
仍是以数据读取为例,操做系统是按块Block(块)从硬盘拿数据,就如同一个大脸盆,一会儿就放入了一盆水。可是,当 Java 使用的时候,旧的 IO(经典IO)确实基于 流 Stream的,也就是虽然操做系统给我了一脸盆水,可是我得用吸管慢慢喝。
因为经典IO的重重落后理念,因而,NIO 横空出世。。。
《Java新一代网络编程模型AIO原理及Linux系统AIO介绍》
《有关“为什么选择Netty”的11个疑问及解答》
《开源NIO框架八卦——究竟是先有MINA仍是先有Netty?》
《选Netty仍是Mina:深刻研究与对比(一)》
《选Netty仍是Mina:深刻研究与对比(二)》
《NIO框架入门(一):服务端基于Netty4的UDP双向通讯Demo演示》
《NIO框架入门(二):服务端基于MINA2的UDP双向通讯Demo演示》
《NIO框架入门(三):iOS与MINA二、Netty4的跨平台UDP双向通讯实战》
《NIO框架入门(四):Android与MINA二、Netty4的跨平台UDP双向通讯实战》
《Netty 4.x学习(一):ByteBuf详解》
《Netty 4.x学习(二):Channel和Pipeline详解》
《Netty 4.x学习(三):线程模型详解》
《Apache Mina框架高级篇(一):IoFilter详解》
《Apache Mina框架高级篇(二):IoHandler详解》
《MINA2 线程原理总结(含简单测试实例)》
《Apache MINA2.0 开发指南(中文版)[附件下载]》
《MINA、Netty的源代码(在线阅读版)已整理发布》
《解决MINA数据传输中TCP的粘包、缺包问题(有源码)》
《解决Mina中多个同类型Filter实例共存的问题》
《实践总结:Netty3.x升级Netty4.x遇到的那些坑(线程篇)》
《实践总结:Netty3.x VS Netty4.x的线程模型》
《详解Netty的安全性:原理介绍、代码演示(上篇)》
《详解Netty的安全性:原理介绍、代码演示(下篇)》
《详解Netty的优雅退出机制和原理》
《NIO框架详解:Netty的高性能之道》
《Twitter:如何使用Netty 4来减小JVM的GC开销(译文)》
《绝对干货:基于Netty实现海量接入的推送服务技术要点》
《Netty干货分享:京东京麦的生产级TCP网关技术实践总结》
《新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析》
《写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略》
《少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别》
更多同类文章 ……
(本文同步发布于:http://www.52im.net/thread-26...)