目录:

Java NIO 学习笔记(一)----概述,Channel/Buffer

Java NIO 学习笔记(二)----聚集和分散,通道到通道

Java NIO 学习笔记(三)----Selector

Java NIO 学习笔记(四)----文件通道和网络通道

Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

Java NIO 学习笔记(七)----NIO/IO 的对比和总结

学完 NIO 和 IO 后,有一个问题:什么时候应该使用 IO,什么时候应该使用 NIO ?本文将尝试阐明 NIO 和 IO 之间的差异,并提供它们的用例,以及它们对程序代码的设计影响。

NIO 和 IO 之间的主要区别

IO NIO
以 Stream 为导向 以 Buffer 为导向
阻塞 IO 非阻塞 IO 选择器

以 Stream 为导向 vs 以 Buffer 为导向

NIO 和 IO 之间的第一个重要区别是 IO 是面向流的,其中 NIO 是面向缓冲区的。 那么,这意味着什么?

面向流的 IO 意味着可以从流中一次读取一个或多个字节,可以按我们的意愿使用读取的字节。 它们不会缓存在任何地方,此外,无法在流中的将数据前后移动。 如果需要将读取的数据前后移动,则需要先将其缓存在缓冲区中。

NIO 的面向缓冲区的方法略有不同。 将数据读入缓冲区,稍后处理该缓冲区。 可以根据需要在缓冲区中前后移动。 这使在处理过程中更具灵活性。 但是,还需检查该缓冲区中是否包含所有需要处理的数据,并且需要确保在将更多数据读入缓冲区时,不会覆盖尚未处理的缓冲区中的数据。

阻塞 IO vs 非阻塞 IO

标准 IO 的各种流都是阻塞的。 这意味着当线程调用 read() 或 write () 时,该线程将被阻塞,直到一些数据被读取或者完全写入,在此期间,线程无法执行任何其他操作。

NIO 的非阻塞模式允许线程请求从通道读取数据,并且只获取当前可用的内容,如果当前没有数据可用,就什么都不读取。 线程可以继续做其他事情,而不是在数据可供读取之前保持阻塞状态。

非阻塞写入也是如此。 线程可以请求将某些数据写入通道,但在完全写入之前不会一直等待它,这样,线程可以在同一时间做继续其他事情。

线程在 IO 操作中没有因为阻塞花费等待时间,通常将等待数据准备的时间用在其他通道上执行 IO 操作。 也就是说,单个线程现在可以管理多个输入和输出通道。

Selector

选择器允许单个线程监视多个输入通道。可以使用选择器注册多个通道,然后使用单个线程“选择”具有可用于处理的输入的通道,或选择准备写入的通道。 这种选择器机制使单个线程可以轻松管理多个通道。

NIO 和 IO 如何影响应用程序设计

无论选择 NIO 还是 IO ,可能都会影响应用程序设计的以下方面:

  1. 对 NIO 或 IO 类的API调用方式
  2. 数据的处理
  3. 用于处理数据的线程数

API 调用方式

当然,使用 NIO 时的 API 调用看起来与使用 IO 时不同。因为必须首先将数据从通道读入缓冲区,然后在缓冲区进行处理,而不是仅仅从 InputStream 读取数据字节。

数据的处理

使用纯 NIO 设计是,对比 IO 设计,数据处理也会受到影响。

在 IO 设计中,从 InputStream 或 Reader 中读取字节的数据字节。 想象一下,正在处理基于行的文本数据流。 例如:

Name: czwbig
Age: 21

这组文本行可以像这样处理:

InputStream input = ... ;
BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String nameLine = reader.readLine();
String ageLine = reader.readLine();

注意处理状态是如何根据程序执行的程度确定的。 换句话说,一旦第一个 reader.readLine() 方法返回,就确定已经读取了整行文本,因为 readLine() 阻塞直到读取完整行,还知道此行包含“Name”。 同样,当第二个 readLine() 调用返回时,可以知道此行包含“Age”等。

所以,只有当有新数据要读取时,程序才会进行,并且对于每个步骤,都知道该读取的数据是什么。 一旦执行的线程已经读取过代码中的某个数据片段,该线程就不会再向后读取旧数据(通常不会)。 下图也说明了此原则:

同上需求,NIO 实现看起来会有所不同。这里有一个简化的例子:

ByteBuffer buffer = ByteBuffer.allocate(64);

int bytesRead = inChannel.read(buffer);

注意第二行从通道读取字节到 ByteBuffer 。 当该方法调用返回时,我们是不知道所需的所有数据是否都已在缓冲区内的,只知道缓冲区包含一些字节。 这使得处理数据变得困难。

想象一下,在第一次读取(缓冲)调用之后,是否所有读入缓冲区的内容都是半行。 例如,“Name:cz”。 你能处理这些数据吗? 显然不能。 在处理任何数据之前,我们需要等待至少一整行数据进入缓冲区。

那么怎么知道缓冲区是否包含足够的数据来处理它?唯一方法是查看缓冲区中的数据。 这样将导致:在知道所有数据是否存在之前,可能需要多次检查缓冲区中的数据(轮询)。 这既低效又可能在程序设计方面变得混乱。 例如:

ByteBuffer buffer = ByteBuffer.allocate(64);

int bytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {
bytesRead = inChannel.read(buffer);
}

bufferFull() 方法必须跟踪读入缓冲区的数据量,并返回 true 或 false ,具体取决于缓冲区是否已满。 换句话说,如果缓冲区已准备好进行处理,则认为它已满。

bufferFull() 方法扫描缓冲区,并且必须使缓冲区保持与调用 bufferFull() 方法之前相同的状态。 如果不这样,则可能无法在正确的位置继续读入下一个数据到缓冲区中。 这不是不可能的,但这是另一个需要注意的问题。

如果缓冲区已满,则可以对其进行处理。 如果缓冲区还没满,有可能让程序先部分处理已到达的数据,这在的特定情况下是有意义的。 但在许多情况下,不完整的数据没有处理的意义。

这个图中说明了 is-data-in-buffer-ready 循环:

总结

NIO 允许仅使用一个(或几个)线程来管理多个通道(网络连接或文件),但成本是解析数据可能比从阻塞流中读取数据时更复杂一些。

如果需要同时管理数千个打开的连接,每个只发送一些数据,例如聊天服务器,这在 NIO 中实现服务器可能是一个优势。 同样,如果需要与其他计算机保持大量开放连接,例如,在 P2P 网络中,使用单个线程来管理所有出站连接可能是一个优势。 下图中说明了这种一个线程,多个连接的设计:

但如果拥有较少带宽的连接,一次连接的数据量较大,那么经典的 IO 服务器实现可能更合适的。 下图说明了这种典型的 IO 服务器设计:

所以,应该根据具体的情况分析,选择更适合的,而不是更新的。

Java NIO 学习笔记(七)----NIO/IO 的对比和总结的更多相关文章

  1. Java NIO学习笔记九 NIO与IO对比

    Java NIO与IO Java nio 和io 到底有什么区别,以及什么时候使用nio和io,本文做一个比较. Java NIO和IO之间的主要区别 下表总结了Java NIO和IO之间的主要区别, ...

  2. java jvm学习笔记七(jar包的代码认证和签名)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 前言: 如果你循序渐进的看到这里,那么说明你的毅力提高了,jvm的很多东西都是比较抽像的,如果不找相对应的代码来辅助理解 ...

  3. Java NIO学习笔记七 Non-blocking Server

    Java NIO:Non-blocking Server 即使你了解了Java NIO非阻塞功能的工作(怎么样Selector,Channel, Buffer等等),设计一个无阻塞服务器仍然很难.非阻 ...

  4. NIO学习笔记七:Pipe

    Java NIO 管道是2个线程之间的单向数据连接.Pipe有一个source通道和一个sink通道.数据会被写到sink通道,从source通道读取. 这里是Pipe原理的图示: 示例代码 Pipe ...

  5. Java NIO学习笔记四 NIO选择器

    Java NIO选择器 A Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已准备就绪,例如读取或写入.这样一个线程可以管理多个通道,从而管理多个网络连接. 为 ...

  6. Java基础学习笔记十九 IO

    File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...

  7. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...

  8. Java基础学习笔记七 Java基础语法之继承和抽象类

    继承 继承的概念 在现实生活中,继承一般指的是子女继承父辈的财产.在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系. 例如公司中的研发部员工和维护部员工都属于员工, ...

  9. Java基础学习笔记二十 IO流

    转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStre ...

  10. 《Thinking in Java》学习笔记(七)

    1.关于反射还有一些需要补充的 package reflect; public class HiddenClass { public A HiddenA(){ return new A(); } } ...

随机推荐

  1. QWidget居中显示(qt窗口坐标原点是在”左上角”的,有图)

    转载请说明出处, 并附上原文链接http://blog.csdn.net/qq907482638/article/details/72189014. 问题描述 在Qt学习过程中,在让QDialog居中 ...

  2. linux的各个子系统

    Linux基本的子系统主要有CPU.Memory.IO.Network. 在这些子系统中,它们之间相互之间高度依赖.不论什么一个子系统的高负载都会引起其它子系统出现故障. 比如: 大量的页调入请求对内 ...

  3. python实现身份证校验位计算

    身份证校验码的计算方法 1.将前面的身份证号码17位数分别乘以不同的系数.第i位对应的数为[2^(18-i)]mod11.从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 ...

  4. 带参跳转其他controller

    public class GoToOtherController : Controller { public ActionResult Index() { var vm = new GetValueF ...

  5. postgresql 自带函数

    替换函数 SELECT replace('abcdefabcdef', 'cd', 'XX') 得到 abXXefabXXef ------------------------------------ ...

  6. DB First EF中的存储过程、函数、视图

    视图约等于表(属性)存储过程变为方法,方法中调用存储过程 EF可以调用存储过程,DB First的流程是刷新模型,获取存储过程,调用参考:http://blog.csdn.net/sudazf/art ...

  7. C# WebClient的使用

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  8. 深入理解Amazon Alexa Skill(三)

    本节来讨论Alexa Skill中涉及到的授权问题. Alexa内功能的授权 Alexa会发给skill用户的token,然后skill代码使用这个token来访问Web API访问用户的Alexa内 ...

  9. erp的核心代码,替代orm

    public static SqlParameter[] get_array_list<T>(ArrayList rows) where T : class { Hashtable sql ...

  10. Android手机导出文件

    因为要写联系人相关的东西,所以得把db导出来看看 第一步:Root手机 尝试了几个Root工具,发现就KingRoot能root 第二个:编写bat脚本 脚本内容是先将DB文件从/data/data ...