IO、NIO区别

Java NIO系列教程(十二) Java NIO与IO

原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html

作者:Jakob Jenkov   译者:郭蕾    校对:方腾飞

当学习了Java NIO和IO的API后,一个问题马上涌入脑海:

我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异、它们的使用场景,以及它们如何影响您的代码设计。

Java NIO和IO的主要区别

下表总结了Java NIO和IO之间的主要差别,我会更详细地描述表中每部分的差异。

IO                NIO
面向流            面向缓冲
阻塞IO            非阻塞IO
无                选择器

面向流与面向缓冲

Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

选择器(Selectors

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

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

无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:

  1.  对NIO或IO类的API调用。
  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

1 BufferedReader reader = new BufferedReader(new InputStreamReader(input));
2  
3 String nameLine   = reader.readLine();
4 String ageLine    = reader.readLine();
5 String emailLine  = reader.readLine();
6 String phoneLine  = reader.readLine();

请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完, readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。 正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则:Java IO: 从一个阻塞的流中读数据) 而一个NIO的实现会有所不同,下面是一个简单的例子:

1 ByteBuffer buffer = ByteBuffer.allocate(48);
2  
3 int bytesRead = inChannel.read(buffer);

注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。
假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,“Name:An”,你能处理数据吗?显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。

所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢?好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:

1 ByteBuffer buffer = ByteBuffer.allocate(48);
2  
3 int bytesRead = inChannel.read(buffer);
4  
5 while(! bufferFull(bytesRead) ) {
6  
7 bytesRead = inChannel.read(buffer);
8  
9 }

bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。

bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。

如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了“缓冲区数据循环就绪”:

Java NIO:从一个通道里读数据,直到所有的数据都读到缓冲区里.

3) 用来处理数据的线程数

NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。

如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个单独的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如下图所示:

Java NIO: 单线程管理多个连接

如果你有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:

Java IO: 一个典型的IO服务器设计- 一个连接通过一个线程处理.

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Java NIO系列教程(十二) Java NIO与IO


<think> 首先,用户的问题是:“我想了解IONIO的相关信息,如概念、区别、使用场景等 请问IONIO的详细介绍、区别及使用场景”。这直接基于系统指令,我需要以中文回答。 系统指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] - 回答结构清晰,帮助用户逐步解决问题。 - 参考站内引用:[^1], [^2], [^3], [^4] 提供了关于IONIO的信息。 用户提供的引用: - [^1]: 比较了IONIO的适用场景:IO适合简单、低并发场景;NIO适合高并发、大数据量场景。 - [^2]: 关于Java IONIO的历史、优缺点和适用场景。 - [^3]: NIO的选择器机制允许单线程管理多个通道。 - [^4]: NIO解决了传统IO中每个连接一个线程的问题,使用线程池或单线程处理多个连接。 我的回答需要结构清晰: 1. 介绍概念:解释IONIO的基本概念。 2. 详细区别:列出IONIO的主要区别。 3. 使用场景:讨论各自适用的场景。 4. 生成相关问题:在末尾添加相关问题。 确保引用:在回答中引用相关部分时,添加引用标识如[^1]。 由于用户提到了“站内引用”,我需要整合这些信息。引用[1]到[4]都是相关的,所以我应该在适当的地方引用它们。 结构建议: - 先定义IONIO。 - 然后对比区别。 - 最后讨论使用场景。 生成相关问题:类似于示例,在回答末尾添加几个相关问题。 确保LaTeX正确:如果提到数学表达式,比如在性能分析中可能有公式,但在这个主题中,可能不涉及太多数学。重点是概念和比较。 回答内容: - **概念**: - IO(Input/Output):传统Java IO模型,基于流(Stream),是阻塞的。每个IO操作阻塞线程。 - NIO(New Input/Output):Java NIO模型,基于通道(Channel)和缓冲区(Buffer),支持非阻塞IO。 - **区别**: - 阻塞 vs 非阻塞:IO是阻塞的;NIO支持非阻塞。 - 线程模型:IO需要每个连接一个线程;NIO使用选择器(Selector)允许单线程管理多个通道。 - 性能:在并发场景下,NIO更高效。 - 适用场景:根据引用[^1],IO适合简单场景如文件读写;NIO适合高并发如网络服务器。 - 缓冲区:NIO使用Buffer,IO使用流。 - **使用场景**: - IO:单用户应用、简单文件操作、低并发网络通信。 - NIO:高并发服务器、大数据量处理、实时系统。 引用整合: - 在区别部分引用[^1], [^3], [^4]。 - 在使用场景部分引用[^1], [^2]。 生成相关问题: - 类似于示例,询问优化、性能分析等。 - 例如: 1. NIO是如何实现非阻塞IO的? 2. 在Java中如何实现一个简单的NIO服务器? 3. IONIO在文件处理中的具体区别是什么? 4. NIO的优化技巧有哪些? 回答格式: - 以中文书写。 - 结构清晰:可能分节如“概念”、“区别”、“使用场景”。 - 结尾添加“相关问题”部分。 最终输出: - 先回答问题。 - 然后“§§相关问题§§”部分。 </think> ### Java IONIO 的详细介绍、区别及使用场景 我将基于您提供的引用资料[^1][^2][^3][^4],系统地介绍 Java IONIO,包括概念、核心区别以及适用场景。回答结构清晰,帮助您逐步理解。 #### 1. **概念介绍** 在 Java 编程中,IO(Input/Output)和 NIO(New Input/Output)是处理输入输出的两种不同模型,适用于不同需求。 - **IO(传统输入输出)**: - IOJava 早期的标准 IO 模型,基于**流(Stream)**概念。它以阻塞方式工作:当一个线程执行 IO 操作(如读取文件或网络数据)时,线程会被阻塞,直到操作完成。IO 模型简单易用,适合处理小规模或顺序性任务。 - 主要组件:包括 `InputStream`、`OutputStream`、`Reader` 和 `Writer` 等类,用于处理字节或字符流。 - **NIO(新输入输出)**: - NIOJava 1.4 引入的改进模型,旨在解决高并发问题。它基于**通道(Channel)**和**缓冲区(Buffer)**,支持非阻塞 IONIO 的核心是**选择器(Selector)**机制,允许单线程监控多个通道,显著减少线程开销[^3][^4]。 - 主要组件:`Channel`(如 `SocketChannel`)、`Buffer`(如 `ByteBuffer`)和 `Selector`,提供高效的数据传输。 #### 2. **核心区别** IONIO 在设计理念和性能上存在显著差异。以下是主要对比点,基于引用资料[^1][^2][^3][^4]的总结: | 特性 | IO | NIO | |------|----|-----| | **阻塞性** | 阻塞模型:每个 IO 操作阻塞线程,直到完成。 | 非阻塞模型:线程可继续执行其他任务,IO 操作异步完成[^1][^4]。 | | **线程模型** | 每个连接需一个独立线程;高并发时线程开销大(栈内存和 CPU 切换成本高)[^4]。 | 使用选择器(Selector)单线程管理多个通道;线程池或单线程即可处理大量连接,减少资源消耗[^3][^4]。 | | **数据传输方式** | 基于流(Stream),数据单向流动(输入/输出分离)。 | 基于通道(Channel)和缓冲区(Buffer),支持双向读写;数据先读入 Buffer,再处理[^2]。 | | **性能表现** | 适用低并发场景:吞吐量较低,线程阻塞导致延迟高。 | 适用高并发场景:吞吐量高,非阻塞机制提升响应速度[^1]。 | | **适用场景** | 简单数据处理,如单用户文件读写或低并发网络通信[^1]。 | 高并发、大数据量应用,如网络服务器、分布式系统[^1][^2]。 | | **复杂度** | 简单易学,API 直观。 | 学习曲线陡峭,需理解 Buffer 和 Selector 机制[^2]。 | #### 3. **使用场景分析** 根据引用资料,IONIO 的适用场景取决于并发需求和数据处理规模: - **IO 的适用场景**: - 低并发应用:例如单用户桌面程序的文件读写(如文本文件处理)、简单网络通信(如客户端小数据传输)。在这些场景中,线程阻塞的开销可忽略,IO 的简单性优势明显[^1][^2]。 - 示例:读取配置文件、写入日志文件或小型 FTP 客户端。 - **NIO 的适用场景**: - 高并发服务器:如 Web 服务器(Apache Tomcat)、实时聊天系统或游戏服务器。NIO 的非阻塞机制允许处理数千并发连接,而无需创建过多线程[^3][^4]。 - 大数据量处理:网络文件传输、视频流媒体,其中 Buffer 机制优化数据传输效率[^2]。 - 分布式系统:微服务架构中的服务间通信,NIO 减少延迟并提高吞吐量[^1]。 - 示例:NIOJava NIO 库(如 Netty 框架)中被广泛用于构建高性能网络应用[^3]。 在实际项目中,选择 IO 还是 NIO 需权衡:如果应用简单且并发低,IO 更高效;反之,高并发场景 NIO 必不可少。例如,引用[^1] 指出,NIO 通过选择器机制“有效地提高系统的吞吐量和性能”,而 IO 在“单用户的文件读写操作”中更合适。 #### 4. **小结** Java IONIO 各有优势:IO 以简单性著称,适合轻量级任务;NIO 以高性能见长,适用于现代高并发系统。理解两者区别是优化 Java 应用的关键。如果您需要代码示例(如使用 NIO 实现简单服务器),可以进一步询问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值