Java NIO (十四)NIO 和 IO 的区别和适用场景分析

简介: 在研究Java NIO和IO API时,很快就会想到一个问题:什么时候应该使用IO,什么时候应该使用NIO?在本文中,我将尝试阐明Java NIO和IO之间的区别,它们的用例以及它们如何影响代码的设计。

在研究Java NIO和IO API时,很快就会想到一个问题:

什么时候应该使用IO,什么时候应该使用NIO?

在本文中,我将尝试阐明Java NIO和IO之间的区别,它们的用例以及它们如何影响代码的设计。

Java NIO和IO之间的主要区别

下表总结了Java NIO和IO之间的主要区别。 我将在表格后面的部分详细介绍每个区别。

IO NIO
面向流 利用缓冲区
阻塞IO 非阻塞IO
Selectors

流导向vs缓冲导向

Java NIO和IO之间的第一大区别是IO是面向流的,其中NIO是面向缓冲区的。 那么,这是什么意思?

面向流的Java IO意味着您一次从流中读取一个或多个字节。 你所做的读字节取决于你。 他们没有被缓存在任何地方。 而且,您不能前后移动数据流。 如果您需要前后移动从流中读取的数据,则需要先将其缓存在缓冲区中。

Java NIO的面向缓冲区的方法略有不同。 数据被读入一个缓冲区,稍后进行处理。 您可以根据需要前后移动缓冲区。 这给你在处理过程中更多的灵活性。 但是,您还需要检查缓冲区是否包含您需要的所有数据,以便对其进行全面处理。 而且,您需要确保在将更多数据读入缓冲区时,不会覆盖尚未处理的缓冲区中的数据。

阻塞vs非阻塞IO

Java IO的各种流都被阻塞。 这意味着,当一个线程调用一个read()或write()时,该线程被阻塞,直到有一些数据要读取,或者数据被完全写入。 线程在此期间不能做其他事情。

Java NIO的非阻塞模式使得线程可以请求从一个通道读取数据,并且只获取当前可用的数据,或者根本没有任何数据可用。 线程可以继续使用别的东西,而不是在数据可用于读取之前保持阻塞状态。

无阻塞写入也是如此。 一个线程可以请求将一些数据写入一个通道,但不要等待它被完全写入。 线程然后可以继续,同时做其他事情。

线程在没有被IO阻塞的情况下花费空闲时间,通常是在其他通道上同时执行IO。 也就是说,一个线程现在可以管理多个输入输出通道。

Selectors

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

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

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

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

API调用

当然,使用NIO时的API调用看起来不同于使用IO时的调用。 这并不奇怪。 而不只是从例如数据字节读取字节。 一个InputStream,数据必须首先被读入一个缓冲区,然后从那里被处理。

数据处理

当使用纯粹的NIO设计时,数据的处理也会受到IO设计的影响。

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

Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

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

InputStream input = ... ; // get the InputStream from the client socket

BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

注意处理状态是由程序执行的程度决定的。 换句话说,一旦第一个reader.readLine()方法返回,您肯定知道已经读取了一整行文本。 readLine()阻塞直到读完整行,这就是为什么。 你也知道这行包含名字。 同样,当第二个readLine()调用返回时,您知道该行包含年龄等

正如你所看到的,只有当有新的数据要读取时,程序才会进行,并且每一步你都知道数据是什么。 一旦正在执行的线程通过读取代码中的某段数据,线程就不会在数据中倒退(大部分不是)。 这个原理在这个图中也有说明:
img_bea7236c40426060b1df64febab4e809.png
image.png

NIO的实现看起来不同。 这是一个简单的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

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

想象一下,如果在第一次读取(缓冲区)调用之后,所有读入缓冲区的内容都是半行。 例如,"Name: An"。 你能处理这些数据吗? 不是真的。 你需要等待,直到完整的一行数据已经进入缓冲区,才有意义处理任何数据。

那么如何知道缓冲区中是否包含足够的数据才能处理? 那么,你没有。 唯一的办法就是查看缓冲区中的数据。 结果是,您可能需要多次检查缓冲区中的数据,才能知道所有数据是否在内部。 这既是低效的,而且在程序设计方面可能变得混乱。 例如:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

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

bufferFull()方法必须跟踪有多少数据被读入缓冲区,并根据缓冲区是否已满而返回true或false。 换句话说,如果缓冲区准备好处理,则认为已满。

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

如果缓冲区已满,可以进行处理。 如果它没有满,那么你可能能够部分处理任何数据,如果这在你的特定情况下是有意义的。 在许多情况下,它不是。

图中显示了is-data-in-buffer-ready循环:


img_c4f506db5724d9ba3cd13e98534fa16a.png
image.png

概要

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

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

如果您的连接带宽非常高,一次发送大量数据,那么传统的IO服务器实现可能是最合适的。 该图说明了一个经典的IO服务器设计:


img_b3564a9bf49be1d5e1c5ed17e5ff6305.png
image.png
相关文章
|
1月前
|
缓存 Linux API
文件IO和标准IO的区别
文件IO和标准IO的区别
20 2
|
1月前
|
存储 Java 数据处理
|
1月前
|
算法 Java
java面向对象和面向过程分析
java面向对象和面向过程分析
36 0
|
1月前
|
存储 Java 编译器
java和c++的主要区别、各自的优缺点分析、java跨平台的原理的深度解析
java和c++的主要区别、各自的优缺点分析、java跨平台的原理的深度解析
75 0
|
1月前
|
Java API
java中IO与NIO有什么不同
java中IO与NIO有什么不同
|
8天前
|
Java 调度
Java中常见锁的分类及概念分析
Java中常见锁的分类及概念分析
14 0
|
8天前
|
Java
Java中ReentrantLock中tryLock()方法加锁分析
Java中ReentrantLock中tryLock()方法加锁分析
10 0
|
25天前
|
人工智能 监控 算法
java智慧城管源码 AI视频智能分析 可直接上项目
Java智慧城管源码实现AI视频智能分析,适用于直接部署项目。系统运用互联网、大数据、云计算和AI提升城市管理水平,采用“一级监督、二级指挥、四级联动”模式。功能涵盖AI智能检测(如占道广告、垃圾处理等)、执法办案、视频分析、统计分析及队伍管理等多个模块,利用深度学习优化城市管理自动化和智能化,提供决策支持。
175 4
java智慧城管源码 AI视频智能分析 可直接上项目
|
8天前
|
Java
Java中关于ConditionObject的signal()方法的分析
Java中关于ConditionObject的signal()方法的分析
21 4
|
8天前
|
Java
Java中关于ConditionObject的分析
Java中关于ConditionObject的分析
17 3