NIO和IO的区别

偶尔善良 提交于 2020-03-01 09:35:06

(NIO翻译单独拎出来)

当我们开始学习IO和NIO的时候,有个问题:我应该在什么时候使用NIO和IO。在本文中我会尽量简明扼要说明NIO和IO的区别,它们的使用环境,以及它们是怎样影响编程。

NIO 和 IO的最主要的区别

以下是它们的主要区别。然后我会详细讲述。

IO NIO
以流为导向    以缓冲为导向
阻塞IO 非阻塞IO
  选择器

Stream Oriented vs. Buffer Oriented

java IO 是流导向的,它意味着你能每次从流中读写一个或多个字节。你要怎么处理你读取的字节取决于你,然而,它们没用被缓存。除此之外,你不能向前或者向后移动流中的数据。如果你需要改变数据读取方向。首先你应该把数据缓存到内存中。NIO和IO 最大区别就是这个。
NIO的缓存导线表现得有点不同。数据被读取到缓冲区中,你能向前或者向后读取数据。这使你更加灵活的处理问题。你能向前或者向后读取数据,除此之外,你也需要检测缓冲区是否包含你需要的数据。你需要确定当你从缓冲区读取数据的时候,你应该确保你所读的东西没有被覆盖过。

阻塞和非阻塞IO

Blocking vs. Non-blocking IO

IO 的所有流处理都是线程阻塞的。这意味着,当一个线程调用读或者写的方法的时候,线程直到数据被读取完或者写入完之前都是阻塞的。这条线程在这端区间里面什么事情也干不了。

NIO的非阻塞模式能够让一条线程去请求从通道读取数据,获取当前有的或者没的的数据。如果没有数据可读,这条线程能做其他事情,而不是保持阻塞状态指导数据成为可读的。

同样的道理适用于非阻塞写。一条线程能请求写入一些数据到通道,但是不必等到完全写入。这条线程能做其他事情。

(以上意思是能边或者边写(或者说等待读或者等待写),然后干一些其他事情。)

NIO一条线程现在能管理多条线程的输入输出。

 

Selectors

NIO的选择器允许一个单线程监管多条通道的输入,你能用一个选择器注册多条通道,然后使用一条线程去“选择”那些已经能进行输入操作通道。或者选择已经准备好了写操作的通道。这种选择器机制能让一条线程能更容易的管理多条线程。

NIO 和IO 是怎么样影响程序设计的

How NIO and IO Influences Application Design

无论你选择NIO 或者 IO 都可能在程序设计时影响下面的方面。

1.API调用NIO或者IO类。

2.数据处理。

3.用于数据处理的线程数。

接口调用

The API Calls

NIO和IO调用方式当然不同。比如说InputStream。相比较与一个字节一个字节的读取。NIO数据首先必须读取到缓冲区。然后进行处理。

数据处理

The Processing of Data

数据处理同样影响着IO和NIO的设计

在IO设计中,你一个字节一个字节地从InputStream或者Reader读取数据。比如说你要处理以下的数据:

 

Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

处理流可能像一下这个样子。

 

InputStream input = ... ; // get the InputStream from the client socket

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()这个方法一直到这整行读取之前都是阻塞的。你同样必须知道这行包含的内容是name。(意思是你必须直到每行要读取的东西是什么。)

如你所看到的,程序只有在有新的数据要读的时候才进行,每一步你都要直到你要读取的数据是什么。一旦正在执行的线程已经处理过一段确定的代码中的数据,这个数据不会退回数据。以下图列可说明。

Java IO: Reading data from a blocking stream.
Java IO: Reading data from a blocking stream.

NIO就不同了,以下是例子。

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

注意第二行是从通道读取到缓冲区。当那个方法返回你不需要直到是否左右你需要的时间已经在缓冲区的数据了。你要知道的就是缓冲区包含一些东西。

想象以下,在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() 检视缓冲区,但是必须保持缓冲区在相同的状态在调用这个方法之前。如果没有,下一个读取到缓冲区的数据可能还没被读取到正确的位置。这不是不可能的。

如果缓冲区满了。它能被处理了,如果还没满,你可能会出路数据的一部分。有时候有意义,有时候没意义。

以下说明:

Java NIO: Reading data from a channel until all needed data is in buffer.
Java NIO: Reading data from a channel until all needed data is in buffer.

Summary

总结

NIO允许你管理多个通道只用一个线程,但是这些花费是解析数据可能比从一个阻塞的线程读取数据更复杂。

如果你需要同时管理数千个打开的连接,而每个连接只发送一小点数据,比如说聊天系统。实现NIO可能更加有利。同样的,如果你需要保持打开大量的连接到其他电脑,比方说P2P网络,使用单线程管理所有外部的连接可能更有利,这样一条线程多个连接可表示为这样:

 

Java NIO: A single thread managing multiple connections.
Java NIO: A single thread managing multiple connections.

 

如果你只有很少的连接到外部系统,每次发送大量数据,可能一个典型的IO服务接口更为合适。以为是图例:

 

Java IO: A classic IO server design - one connection handled by one thread.
Java IO: A classic IO server design - one connection handled by one thread.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!