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 ...
随机推荐
- shell 中的通配符:
shell 中的通配符: *: 代表 0 个或者多个任意字符 ?: 代表一定有一个的任意字符 []: 代表一定有一个在括号内的字符(非任意字符).例如[abcd]代表一定有一个字符,可能是 abcd ...
- okhttp缓存策略源码分析:put&get方法
对于OkHttp的缓存策略其实就是在下一次请求的时候能节省更加的时间,从而可以更快的展示出数据,那在Okhttp如何使用缓存呢?其实很简单,如下: 配置一个Cache既可,其中接收两个参数:一个是缓存 ...
- 2019 Petrozavodsk Winter Camp, Yandex Cup C. Diverse Singing 上下界网络流
建图一共建四层 第一层为N个歌手 第二层为{pi,li} 第三层为{si,li} 第四层为M首歌 除了S和第一层与第三层与T之间的边为[1,INF] 其他边均为[0,1] #include<bi ...
- [转载]pytest学习笔记
pytest学习笔记(三) 接着上一篇的内容,这里主要讲下参数化,pytest很好的支持了测试函数中变量的参数化 一.pytest的参数化 1.通过命令行来实现参数化 文档中给了一个简单的例子, ...
- linux 没有界面内容显示不全解决办法
1.管道 管道简单理解就是,使用管道意味着第一个命令的输出会作为第二个命令的输入,第二个命令的输出又会作为第三个命令的输入,依此类推.利用Linux所提供的管道符“|”将两个命令隔开,管道符左边命令的 ...
- TCP/IP协议簇 端口 三次握手 四次挥手 11种状态集
第一章:概念介绍 1.1 VLAN 1.1.1 什么是VLAN VLAN (Virturl LAN) ,翻译成中文是:“虚拟局域网”.VLAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计 ...
- 用vs2013开启一个C拖控件的项目
visual studio作为一款集成开发环境备受青睐,笔者尤其喜爱它的拖控件功能,程序员应该追求业务逻辑和实际功能的优化,而不是把时间消耗在编写窗体和按钮上 笔者曾翻阅中关村图书大厦,西单图书大厦, ...
- C# Tcp和Socket 网络(五)
TcpReceive public Form1() { InitializeComponent(); new Thread(() => { IPAddress ip = IPAddress.Pa ...
- kafka接口文档和kafka教程
http://kafka.apache.org/090/javadoc/index.html?org/apache/kafka/clients/consumer/KafkaConsumer.html ...
- 本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用<image/>标签
在微信小程序开发中,当在CSS中使用背景图片格式为png时就会出现: 只要把png格式改掉就可以或者在<image/>标签里面写,我实测用JPG格式和把图片转成base64是没问题的.