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
相关文章
|
8月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
235 0
|
5月前
|
Java Unix Go
【Java】(8)Stream流、文件File相关操作,IO的含义与运用
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。!但本节讲述最基本的和流与 I/O 相关的功能。我们将通过一个个例子来学习这些功能。
244 1
|
7月前
|
Java 测试技术 API
Java IO流(二):文件操作与NIO入门
本文详解Java NIO与传统IO的区别与优势,涵盖Path、Files类、Channel、Buffer、Selector等核心概念,深入讲解文件操作、目录遍历、NIO实战及性能优化技巧,适合处理大文件与高并发场景,助力高效IO编程与面试准备。
|
8月前
|
存储 Java Linux
操作系统层面视角下 Java IO 的演进路径及核心技术变革解析
本文从操作系统层面深入解析Java IO的演进历程,涵盖BIO、NIO、多路复用器及Netty等核心技术。分析各阶段IO模型的原理、优缺点及系统调用机制,探讨Java如何通过底层优化提升并发性能与数据处理效率,全面呈现IO技术的变革路径与发展趋势。
171 2
|
7月前
|
SQL Java 数据库连接
Java IO流(一):字节流与字符流基础
本文全面解析Java IO流,涵盖字节流、字符流及其使用场景,帮助开发者理解IO流分类与用途,掌握文件读写、编码转换、异常处理等核心技术,通过实战案例提升IO编程能力。
|
12月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
522 23
|
缓存 网络协议 Java
JAVA网络IO之NIO/BIO
本文介绍了Java网络编程的基础与历史演进,重点阐述了IO和Socket的概念。Java的IO分为设备和接口两部分,通过流、字节、字符等方式实现与外部的交互。
435 0
|
存储 Java 数据安全/隐私保护
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
|
Java 数据处理 开发者
揭秘Java IO流:字节流与字符流的神秘面纱!
揭秘Java IO流:字节流与字符流的神秘面纱!
210 1
|
自然语言处理 Java 数据处理
Java IO流全解析:字节流和字符流的区别与联系!
Java IO流全解析:字节流和字符流的区别与联系!
622 1