Java NIO学习笔记九 NIO与IO对比
Java NIO与IO
Java nio 和io 到底有什么区别,以及什么时候使用nio和io,本文做一个比较。
Java NIO和IO之间的主要区别
下表总结了Java NIO和IO之间的主要区别,在下面我们详细看两者的区别。
IO | NIO |
面向流 | 面向缓冲区 |
阻止IO | 非阻塞IO |
选择器 |
面向流VS面向缓冲区
Java NIO和IO之间的第一差异在于IO是面向流的,其中NIO是面向缓冲的。
面向流的Java IO意味着您一次从流中读取一个或多个字节。你读取的字节取决于你所做的。他们没有任何缓存空间。此外,你不能向前或向后移动流中的数据。如果您需要在从流中读取的数据中前后移动,则需要首先将其缓存在缓冲区中。
Java NIO的缓存导向方法略有不同。数据先被读入缓存区然后再被处理。您可以根据需要在缓冲区中向前后移动。这样可以在处理过程中给予您更多的灵活性。但是,您还需要检查缓冲区是否包含所有需要的数据,以便完全处理它。而且,您需要确保在缓冲区中读取更多数据时,不要覆盖尚未处理的在缓冲区中的数据。
阻塞与非阻塞IO
Java IO的流是阻塞的。这意味着,当一个线程调用一个read()
或者write(),意味着这个
线程被阻塞,直到有一些数据被读取或数据被完全写入。在此期间,线程也不能做任何事情。
Java NIO的非阻塞模式使线程能够请求从通道读取数据,如果当目前没有数据可用时,只能获得当前可用的数据,或者没有任何信息。直到数据可用于阅读,线程可以继续其他。
非阻塞写入也是如此。一个线程可以请求将数据写入一个通道,但不等待它被完全写入。然后,线程可以继续,并在同一时间做其他事情。
在IO调用中未阻塞时,线程花费空闲时间,通常在其他频道上执行IO。所以,单线程现在可以管理多个输入和输出通道。
选择器
Java NIO的选择器允许单个线程监视多个通道的输入。您可以使用选择器注册多个通道,然后使用单个线程“选择”具有可用于处理的输入的通道,或选择准备好进行写入的通道。这种选择器机制使单线程更容易管理多个通道。
NIO和IO如何影响应用程序设计
是否选择NIO或IO作为IO工具包可能会影响应用程序设计的以下方面:
A-API调用NIO或IO类
B-处理数据过程
C-用于处理数据的线程数
API方法调用
当使用NIO调用API看起来不同于使用IO时。例如从InputStream读取字节的数据字节,数据必须首先被读入缓冲区,然后再做处理。
数据处理过程
使用纯NIO设计对比使用IO设计,数据处理也会受到影响。
在IO设计中,您可以从InputStream
或Reader读取字节数据的字节。假设,您正在处理基于行的文本数据流。
例如:
Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890
文本行流可以这样处理:
InputStream input = ...; //从客户端套接字获取InputStream 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()
通话返回时,你知道这一行包含年龄等。
您可以看到,只有当有新的数据读取时,程序才会进行,对于每一步,您都可以知道该数据。一旦执行的线程在代码中读取了一段数据之后,该线程就不会在数据中倒退(大多数情况下不是)。该原理也在图中说明:
![]() |
Java IO:从阻塞流读取数据。 |
NIO实现看起来会有所不同。这是一个简化的例子:
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer);
注意从通道读入字节的第二行ByteBuffer
。当该方法调用返回时,您不知道所有需要的数据是否在缓冲区内。所有你知道的是缓冲区包含一些字节。这使处理更难。
想象一下,如果在第一次read(buffer)
调用之后,所有读入缓冲区的内容都是半行。例如“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循环如图所示:
![]() |
Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中。 |
概要
NIO允许您仅使用单个(或少数)线程来管理多个通道(网络连接或文件),但成本是从阻止流中读取数据时,解析数据可能会稍微复杂一些。
如果您需要同时管理数千个开放式连接,哪些只发送一些数据,例如聊天服务器,在NIO中实现服务器可能是一个优势。同样,如果您需要保持与其他计算机的许多开放连接,例如在P2P网络中,则使用单个线程来管理所有出站连接可能是一个优点。这一个线程,多连接设计如图所示:
![]() |
Java NIO:管理多个连接的单线程。 |
如果您具有较少带宽的连接,一次发送大量数据,则传统的IO服务器实现可能是最合适的。该图说明了传统的IO服务器设计:
![]() |
Java IO:经典的IO服务器设计 - 一个线程处理的一个连接。 |
Java NIO与IO
当学习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。也就是说,单线程现在可以管理多个输入和输出通道。
选择
Java NIO的选择器允许单个线程监视多个通道的输入。您可以使用选择器注册多个通道,然后使用单个线程“选择”具有可用于处理的输入的通道,或选择准备好进行写入的通道。这种选择器机制使单线程易于管理多个通道。
NIO和IO如何影响应用程序设计
您是否选择NIO或IO作为IO工具包可能会影响应用程序设计的以下方面:
- API调用NIO或IO类。
- 处理数据。
- 用于处理数据的线程数。
API通话
当使用NIO看起来不同于使用IO时,API调用。这并不奇怪 而不是从例如a读取字节的数据字节InputStream
,数据必须首先被读入缓冲区,然后从那里处理。
数据处理
使用纯NIO设计,而不是IO设计,数据处理也会受到影响。
在IO设计中,您可以从InputStream
a或a 读取字节的数据字节Reader
。想象一下,您正在处理基于行的文本数据流。例如:
name:ana
age:25岁
email:anna@mailserver.com
ptone:1234567890
文本行流可以这样处理:
InputStream input = ...; //从客户端套接字获取InputStream 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()
通话返回时,你知道这一行包含年龄等。
您可以看到,只有当有新的数据读取时,程序才会进行,对于每一步,您都可以知道该数据。一旦执行的线程在代码中读取了一段数据之后,该线程就不会在数据中倒退(大多数情况下不是)。该原理也在图中说明:
![]() |
Java IO:从阻塞流读取数据。 |
NIO实现看起来会有所不同。这是一个简化的例子:
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer);
注意从通道读入字节的第二行ByteBuffer
。当该方法调用返回时,您不知道所有需要的数据是否在缓冲区内。所有你知道的是缓冲区包含一些字节。这使处理更难。
想象一下,如果在第一次read(buffer)
调用之后,所有读入缓冲区的内容都是半行。例如“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循环如图所示:
![]() |
Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中。 |
概要
NIO允许您仅使用单个(或少数)线程来管理多个通道(网络连接或文件),但成本是从阻止流中读取数据时,解析数据可能会稍微复杂一些。
如果您需要同时管理数千个开放式连接,哪些只发送一些数据,例如聊天服务器,在NIO中实现服务器可能是一个优势。同样,如果您需要保持与其他计算机的许多开放连接,例如在P2P网络中,则使用单个线程来管理所有出站连接可能是一个优点。这一个线程,多连接设计如图所示:
![]() |
Java NIO:管理多个连接的单线程。 |
如果您具有较少带宽的连接,一次发送大量数据,则传统的IO服务器实现可能是最合适的。该图说明了传统的IO服务器设计:
![]() |
Java IO:经典的IO服务器设计 - 一个线程处理的一个连接。 |
Java NIO学习笔记九 NIO与IO对比的更多相关文章
- NIO学习笔记,从Linux IO演化模型到Netty—— Java NIO零拷贝
同样只是大致上的认识. 其中,当使用transferFrom,transferTo的时候用的sendfile(). 如果系统内核不支持 sendfile,进一步执行 transferToTrusted ...
- NIO学习笔记,从Linux IO演化模型到Netty—— 从BIO到epoll模型
本文不涉及具体代码,只分析Linux IO演化的心路历程,学习资料来源网络,不保证一定正确,若有错误,欢迎指出. BIO 服务端创建socket(80端口),文件描述符3号. 当线程调用accept时 ...
- Java NIO学习笔记四 NIO选择器
Java NIO选择器 A Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已准备就绪,例如读取或写入.这样一个线程可以管理多个通道,从而管理多个网络连接. 为 ...
- NIO学习笔记,从Linux IO演化模型到Netty—— Linux零拷贝
这里只是感性地认识Linux零拷贝,不涉及具体细节. 1.Linux传统的数据拷贝 用户进程是不能直接访问文件系统的,要先切换到内核态,发起系统调用,DMA把磁盘中的数据写入内核空间,内核再把数据拷贝 ...
- NIO学习笔记,从Linux IO演化模型到Netty—— 究竟如何理解同步、异步、阻塞、非阻塞
我的观点 首先,分开各自理解. 1. 同步:描述两个(或者多个)个体之间的协调关系. 比如,单线程中,methodA调用了methodB,methodB返回后,methodA才往下执行,那么称A同步调 ...
- NIO学习笔记,从Linux IO演化模型到Netty—— Netty零拷贝
Netty的中零拷贝与上述零拷贝是不一样的,它并不是系统层面上的零拷贝,只是相对于ByteBuf而言的,更多的是偏向于数据操作优化这样的概念. Netty中的零拷贝: 1.CompositeByteB ...
- java jvm学习笔记九(策略文件)
欢迎装载请说明出处:http://blog.csdn.net/yfqnihao/article/details/8271407 课程源码:http://download.csdn.net/detail ...
- Java基础学习笔记九 Java基础语法之this和super
构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方法.当 ...
- java 多线程学习笔记(二) -- IO密集型任务
IO密集型是指对IO操作较多的任务.下面以查询一些股票价格任务为例: YahooFinance.java public class YahooFinance { public static doubl ...
随机推荐
- Hadoop_28_MapReduce_自定义 inputFormat
1. 自定义inputFormat 1.1.需求: 无论hdfs还是mapreduce,对于小文件都有损效率,实践中,又难免面临处理大量小文件,此时就需要有相应解决方案; 1.2.分析: 小文件的优化 ...
- java 项目坑记录
1. spring项目引入了lombok jar包,且已在类上面添加@Data注释,还是关联不到 get() 和 set() 方法 需要安装扩展包 如果在线安装失败,提示错误readtime out, ...
- Python3+Appium学习笔记01-环境配置(上)
公司可能也有关于对app自动化的一些想法,让我去研究下.当然以移动互联网的热度.对于app自动化测试技术听闻已久.也一直想要去学习.正好.这次可以在工作时间中学习.emmm.希望自己能坚持把这个系列更 ...
- Java抽象类 详解
一.抽象类的基本概念 普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法.普通方法.static方法.常量和变量等内容.而抽象类是指在普通类的结构里面增加抽象方法的组成 ...
- 16-SQLServer强制走索引
一.注意点 1.使用with(index(索引名称))来使SQL强制走索引. 二.示例截图 1.创建非聚集索引 2.不使用with,不走索引的截图 3.使用with,强制走索引的截图
- Thread setUncaughtExceptionHandler
setUncaughtExceptionHandler 用于获取线程运行时异常 线程在执行时是不能抛出 checked 异常的,IDE 只会提示你用 try-catch 包裹起来.因此主线程无法直接获 ...
- springBoot+websocket集群系列知识
WebSocket简介和spring boot集成简单消息代理 Spring Boot 集成 websocket,使用RabbitMQ做为消息代理 Spring Websocket实现向指定的用户发送 ...
- istio 安装与bookinfo示例运行
目的 本文旨在帮助想了解istio安装和运行bookinfo示例的同学快速入门 前置准备 安装k8s和helm 1.k8s安装 修改主机名 hostnamectl set-hostname k8s-m ...
- Linux shell -"a-d"命令
shell中条件判断if中的-z到-d的意思 分类:shellLinux (2006) (0) shell中条件判断if中的-z到-d的意思 [ -a FILE ] 如果 FILE 存在则为真. ...
- Codeforces 1051 D.Bicolorings(DP)
Codeforces 1051 D.Bicolorings 题意:一个2×n的方格纸,用黑白给格子涂色,要求分出k个连通块,求方案数. 思路:用0,1表示黑白,则第i列可以涂00,01,10,11,( ...