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

  • 时间:2018-12-02 23:18 作者:czwbig 来源:czwbig 阅读:461
  • 扫一扫,手机访问
摘要:目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Selector Java NIO 学习笔记(四)----文件通道和网络通道 Java NIO

目录:
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 之间的主要区别

IONIO
以 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: czwbigAge: 21

这组文本行可以像这样解决:

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

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

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

image

同上需求,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 循环:

image

总结

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

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

image

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

image

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

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|软件环境】梦幻仙域游戏攻略(2023-12-19 10:02)
【系统环境|软件环境】梦幻仙域游戏攻略(2023-12-19 10:02)
【系统环境|】卡帕部落揭秘潮玩新宠,探究玩法(2023-12-14 09:45)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】如何开发搭建卡帕部落模式源码(2023-12-12 10:44)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
手机二维码手机访问领取大礼包
返回顶部