• 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服务器设计 - 一个线程处理的一个连接。
  • 相关阅读:
    设计模式学习笔记--迭代器模式
    设计模式学习笔记--组合模式
    设计模式学习笔记--备忘录模式
    Asp.Net Core IdentityServer4 中的基本概念
    Asp.Net Core 中间件应用实践中你不知道的那些事
    Asp.Net Core Filter 深入浅出的那些事-AOP
    ASP.NET CORE 内置的IOC解读及使用
    ASP.NET CORE 管道模型及中间件使用解读
    ASP.NET CORE 启动过程及源码解读
    Linux +Docker +Nginx 部署代理转发初探
  • 原文地址:https://www.cnblogs.com/kuoAT/p/7028598.html
Copyright © 2020-2023  润新知