Java 输入输出流NIO与IO的区别

原文:https://blog.csdn.net/java852987/article/details/82869656

Java入门在线学习,学不会退款。

面向流和面向缓冲

  • NIO和IO之间的一个最大的区别就是,IO是面向流的,而NIO是面向缓冲区的。
  • IO是面向流的,意味着每次从流中读取一个或多个字节,直到读取所有字节,它们没有被缓存在任何地方。此外,流是单向的,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,则需要把数据缓存到一个缓存区中。
  • NIO是面向缓冲区的,数据被读取到一个它稍后处理的缓冲区,需要时可以在缓冲区中前后移动。这就增加了处理过程中的灵活性。需要注意的是,我们需要检查缓冲区中是否有我们需要的数据。而且,需确保当更多的数据读入缓冲区时,尚未处理的数据不会被覆盖。

阻塞和非阻塞

  • IO的各种流都是阻塞的。这意味着,当一个线程调用read()或write()方法,该线程将被阻塞,直到有数据被读取,或者数据完全写入。该线程在此期间不能再干任何事情了。
  • NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能获取目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所有直到数据变得可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入数据到某个通道,但它不会等待它将数据完全写入,这个线程同时可以去做其他事情。线程通常将在阻塞IO的空闲时间用于其他通道上执行IO操作,所以一个单独的线程现在可以用于管理多个输入和输入通道。

选择器

选择器是NIO独有的组件。我们可以注册多个通道来使用一个选择器,然后使用一个线程来管理选择器。选择器可以监视这些通道中已经可以处理的输入,或者已准备写入的通道。这种选择机制,是的一个单独的线程可以用来管理多个通道。

NIO和IO对程序设计的影响

API调用

NIO和IO的API调用不同,但这并不意外,因为IO是逐字节读取的,NIO是先读取缓冲区在进行处理的。

数据处理

  • 使用NIO设计较IO设计,数据处理也收到影响。
  • 在IO设计中,我们从InputStream或OutputStream逐字节读取数据。如下基于行的文本数据流:
Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

基于IO设计处理如下:

BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine = reader.readLine();
String ageLine = reader.readLine();
String emailLine = reader.readLine();
String phoneLine = reader.readLine();

readLine()方法会阻塞直到整行读完,一旦readLine()返回,你就知道文本行已读完。你会知道第一行包含名称。我们知道每步的数据是什么。一旦我们处理过某些数据,该线程不会在回退数据。如图所示:
IO处理
而NIO的实现有所不同,如下面一个简单的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);

  
  

在第二行,我们读取数据到Buffer中。当这个方法返回时,我们并不知道所需的数据是否在缓存区中。我们仅仅知道的是,该缓冲区包含一下字节,这使得处理有些困难。
假设第一次read(buffer)调用后,读入缓冲区的数据只有半行,如“Name:An”,你能处理数据吗?显然不能,我们需要等待,知道整行数据都被读取缓存,在此之前,对数据的处理都毫无意义。
那么,我们怎么知道缓冲区的数据是否包含足够的数据可用处理呢?我们只能不断的检查几次缓存区的数据。这不仅效率低下,而且使得程序设计杂乱不堪。如下:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {
bytesRead = inChannel.read(buffer);
}

bufferFull()方法必须跟踪有多少数据写入缓冲区,并返回真假,这取决于缓冲区是否已满。如果缓冲区已满,它可以被处理。
通过bufferFull()方法扫描缓冲区,缓冲区必须保持和bufferFull()方法被调用之前相同的状态,如果不是,下一个读入缓冲区的数据可能无法被读到正确的位置,这并非不可能,但也暴露出另一个需要我们注意的问题。如下图:
NIO处理

用来处理数据的线程数

  • NIO可以用一个(或几个)单线程来管理多个通道(网络连接或文件),但代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
  • 如果需要同时管理打开的成千上万个连接,这些连接每次只是发送少量的数据,如聊天服务器,NIO可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络上,使用一个单独的线程来管理所有的出站连接,可能是一个优势。一个线程多个连接设计方案如下所示:
    一个线程多个连接
  • 如果有少量的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:
    典型IO

多路复用IO的优缺点

  • 同一个端口可以处理多种协议。例如,使用ServerSocketChannel的服务器端口监听,可以处理TCP协议又可以处理UDP协议。
  • 都是同步IO:阻塞式IO,非阻塞式IO甚至包括多路复用IO,这些都是基于操作系统对同步IO的实现。同步IO的意思是,只有上层(包括上层的某种代理机制)系统询问我是否有某个事件发生了,否则我不会主动告诉上层系统事件发生了。
    参考并发编程网
        </div>

猜你喜欢

转载自blog.csdn.net/baidu_38868875/article/details/89532362