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()方法具有跟踪的多少数据读入缓冲区,并返回无论是truefalse,取决于缓冲区是否已满。换句话说,如果缓冲区准备好处理,那么它被认为是满的。

  该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工具包可能会影响应用程序设计的以下方面:

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

API通话

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

数据处理

使用纯NIO设计,而不是IO设计,数据处理也会受到影响。

在IO设计中,您可以从InputStreama或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()方法具有跟踪的多少数据读入缓冲区,并返回无论是truefalse,取决于缓冲区是否已满。换句话说,如果缓冲区准备好处理,那么它被认为是满的。

bufferFull()方法扫描缓冲区,但必须使缓冲区与bufferFull()调用该方法之前的状态相同。如果没有,读入缓冲区的下一个数据可能不会被读入正确的位置。这不是不可能的,但这是另一个值得注意的问题。

如果缓冲区已满,则可以进行处理。如果不完整,您可能能够部分处理任何数据,如果这在您的具体情况下有意义。在许多情况下,它不是。

is-data-in-buffer-ready循环如图所示:

Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中。

概要

NIO允许您仅使用单个(或少数)线程来管理多个通道(网络连接或文件),但成本是从阻止流中读取数据时,解析数据可能会稍微复杂一些。

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

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

如果您具有较少带宽的连接,一次发送大量数据,则传统的IO服务器实现可能是最合适的。该图说明了传统的IO服务器设计:

Java IO:经典的IO服务器设计 - 一个线程处理的一个连接。

Java NIO学习笔记九 NIO与IO对比的更多相关文章

  1. NIO学习笔记,从Linux IO演化模型到Netty—— Java NIO零拷贝

    同样只是大致上的认识. 其中,当使用transferFrom,transferTo的时候用的sendfile(). 如果系统内核不支持 sendfile,进一步执行 transferToTrusted ...

  2. NIO学习笔记,从Linux IO演化模型到Netty—— 从BIO到epoll模型

    本文不涉及具体代码,只分析Linux IO演化的心路历程,学习资料来源网络,不保证一定正确,若有错误,欢迎指出. BIO 服务端创建socket(80端口),文件描述符3号. 当线程调用accept时 ...

  3. Java NIO学习笔记四 NIO选择器

    Java NIO选择器 A Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已准备就绪,例如读取或写入.这样一个线程可以管理多个通道,从而管理多个网络连接. 为 ...

  4. NIO学习笔记,从Linux IO演化模型到Netty—— Linux零拷贝

    这里只是感性地认识Linux零拷贝,不涉及具体细节. 1.Linux传统的数据拷贝 用户进程是不能直接访问文件系统的,要先切换到内核态,发起系统调用,DMA把磁盘中的数据写入内核空间,内核再把数据拷贝 ...

  5. NIO学习笔记,从Linux IO演化模型到Netty—— 究竟如何理解同步、异步、阻塞、非阻塞

    我的观点 首先,分开各自理解. 1. 同步:描述两个(或者多个)个体之间的协调关系. 比如,单线程中,methodA调用了methodB,methodB返回后,methodA才往下执行,那么称A同步调 ...

  6. NIO学习笔记,从Linux IO演化模型到Netty—— Netty零拷贝

    Netty的中零拷贝与上述零拷贝是不一样的,它并不是系统层面上的零拷贝,只是相对于ByteBuf而言的,更多的是偏向于数据操作优化这样的概念. Netty中的零拷贝: 1.CompositeByteB ...

  7. java jvm学习笔记九(策略文件)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao/article/details/8271407 课程源码:http://download.csdn.net/detail ...

  8. Java基础学习笔记九 Java基础语法之this和super

    构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方法.当 ...

  9. java 多线程学习笔记(二) -- IO密集型任务

    IO密集型是指对IO操作较多的任务.下面以查询一些股票价格任务为例: YahooFinance.java public class YahooFinance { public static doubl ...

随机推荐

  1. Hadoop_28_MapReduce_自定义 inputFormat

    1. 自定义inputFormat 1.1.需求: 无论hdfs还是mapreduce,对于小文件都有损效率,实践中,又难免面临处理大量小文件,此时就需要有相应解决方案; 1.2.分析: 小文件的优化 ...

  2. java 项目坑记录

    1. spring项目引入了lombok jar包,且已在类上面添加@Data注释,还是关联不到 get() 和 set() 方法 需要安装扩展包 如果在线安装失败,提示错误readtime out, ...

  3. Python3+Appium学习笔记01-环境配置(上)

    公司可能也有关于对app自动化的一些想法,让我去研究下.当然以移动互联网的热度.对于app自动化测试技术听闻已久.也一直想要去学习.正好.这次可以在工作时间中学习.emmm.希望自己能坚持把这个系列更 ...

  4. Java抽象类 详解

    一.抽象类的基本概念 普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法.普通方法.static方法.常量和变量等内容.而抽象类是指在普通类的结构里面增加抽象方法的组成 ...

  5. 16-SQLServer强制走索引

    一.注意点 1.使用with(index(索引名称))来使SQL强制走索引. 二.示例截图 1.创建非聚集索引 2.不使用with,不走索引的截图 3.使用with,强制走索引的截图

  6. Thread setUncaughtExceptionHandler

    setUncaughtExceptionHandler 用于获取线程运行时异常 线程在执行时是不能抛出 checked 异常的,IDE 只会提示你用 try-catch 包裹起来.因此主线程无法直接获 ...

  7. springBoot+websocket集群系列知识

    WebSocket简介和spring boot集成简单消息代理 Spring Boot 集成 websocket,使用RabbitMQ做为消息代理 Spring Websocket实现向指定的用户发送 ...

  8. istio 安装与bookinfo示例运行

    目的 本文旨在帮助想了解istio安装和运行bookinfo示例的同学快速入门 前置准备 安装k8s和helm 1.k8s安装 修改主机名 hostnamectl set-hostname k8s-m ...

  9. Linux shell -"a-d"命令

    shell中条件判断if中的-z到-d的意思 分类:shellLinux (2006)  (0) shell中条件判断if中的-z到-d的意思  [ -a FILE ] 如果 FILE 存在则为真.  ...

  10. Codeforces 1051 D.Bicolorings(DP)

    Codeforces 1051 D.Bicolorings 题意:一个2×n的方格纸,用黑白给格子涂色,要求分出k个连通块,求方案数. 思路:用0,1表示黑白,则第i列可以涂00,01,10,11,( ...