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设计中,您可以从InputStreamReader读取字节数据的字节。假设,您正在处理基于行的文本数据流。

例如:

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:从阻塞流读取数据。
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:从通道读取数据,直到所有需要的数据都在缓冲区中。
Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中。

概要

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

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

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

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

Java 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:从阻塞流读取数据。
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:从通道读取数据,直到所有需要的数据都在缓冲区中。
Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中。

概要

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

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

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

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

Java IO:经典的IO服务器设计 - 一个线程处理的一个连接。
Java IO:经典的IO服务器设计 - 一个线程处理的一个连接。
原文地址:https://www.cnblogs.com/kuoAT/p/7028598.html