欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Java NIO 简例】NIO vs IO

程序员文章站 2022-07-13 16:53:27
...

原文:《Java NIO vs. IO

当学习Java NIO 和 IO 的API时,很快会遇到一个问题:
什么时候用IO,什么时候用NIO?

我会尝试在本文提供一些关于Java NIO 与 IO 的不同点、使用案例、及它们将如何影响代码设计方面的见解。

 

NIO 与 IO 的主要不同

下表总结了Java NIO 和 IO 的主要不同。我会在后文详细论述每一个不同点。

IO NIO
面向 Stream 面向 Buffer 
阻塞式IO 非阻塞IO
  Selector

 

面向Stream vs 面向Buffer

NIO 和 IO的第一个最大不同是:IO面向Stream,NIO面向Buffer。这意味着什么?

 

IO 面向 Stream 意味着你可以从stream一次读取一个或多个字节。读多少个字节取决于你。它们不会被缓存在任何地方。进一步而言,你无法在stream数据流中往前或往后移动。如果你需要在这些数据中往前或往后移动,你需要先将其缓存在一个buffer中。

 

NIO 面向 Buffer 稍微有点不同。数据被读到一个buffer中,然后再被处理。你可以根据需要在这个buffer中往前或往后移动。这给了你更多处理过程中的扩展性。但是你也需要检查buffer是否包含了进行完整处理所需的所有数据。而且你需要确保往buffer读入更多数据时不会覆盖掉那些尚未被处理的数据。

 

阻塞 vs 非阻塞

Java IO 的各种Stream是阻塞式的。这意味着,当线程调用了 read() 或 write() 方法,该线程会被阻塞,直到有数据可读,或所有数据被写入。在此期间,该线程无法做其它任何事情。

 

Java NIO 的非阻塞模式允许线程从channel请求数据,且只会得到当前就绪的数据;如果当前没有就绪的数据,就得不到任何数据。且该线程不会被阻塞直到有数据就绪可读,而是会理解返回并可以做其它事情。

非阻塞写数据也类似。线程可以请求向channel写入数据,但不会被阻塞直到所有数据被写入。线程可以继续做其它事情。

线程处于空闲未被阻塞于IO调用时,通常可以处理其它channel的IO。也就是说现在单线程可以处理多channel的输入和输出。

 

Selector

Selector 允许单线程监视多个 Channel。你可以将多个 Channel 注册到一个 Selector 上,让后用单个线程“选出”就绪可读或可写的 Channel。此 Selector 机制使得单线程管理多Channel更容易。

 

NIO 和 IO 如何影响应用设计

选择 NIO 或 IO 会在以下几个方面影响你的应用设计:

  1. 调用的API(来自NIO的类还是IO的类)
  2. 处理数据的过程
  3. 处理数据的线程数

调用的API

毫不意外,使用NIO与IO所调用的API是不同的。在NIO中,数据必须先被读到一个Buffer中,然后再处理;在IO中,数据是被直接从Stream读出来进行处理的。

 

处理数据的过程

IO模式

代码示例:

// InputStream input = ...
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line1 = reader.readLine();
String line2 = reader.readLine();

【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
 

NIO模式

代码示例:

ByteBuffer buffer = ByteBuffer.allocate(1024);
int readByteCount = channel.read(buffer);

while (!isDataComplete(readByteCount)) {
  readByteCount = channel.read(buffer);
}

channel.read(buffer) 所读取到的数据量是不一定的,可能这些数据并不足以用于后续业务的执行,而需要多次执行读取操作以获得义务意义上完整的数据。
这就引入了低效的数据完整性检查操作,及凌乱复杂的程序设计:

  • isDataComplete(int) 方法用于检查数据是否足以用于后续业务执行;
  • while 循环直到获得足够的数据量。

【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
 

总结

NIO 允许你用单个(或更多)线程管理多个 Channel (如,网络连接 或 文件)。但是其代价是更复杂的数据解析操作(相比与从阻塞Stream中读取数据)。

 

# 如果你需要同时管理上千个连接,且这些连接都只发送少量的数据(如,聊天系统的服务端),那么用NIO实现服务端可能会有优势。
同样的,如果你需要保持大量与其它机器的连接(如,P2P网络),那么用单个线程管理所有向外的连接可能有优势。
这个 单线程-多连接 的设计简化如下:

【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
 

 

# 如果连接较少,且都占用较高的带宽,单次传输的数据量非常多,也许一个典型的IO服务端实现最合适。设计简化如下:

【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 

  • 【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
  • 大小: 24.8 KB
  • 【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
  • 大小: 17.9 KB
  • 【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
  • 大小: 16 KB
  • 【Java NIO 简例】NIO vs IO
            
    
    博客分类: Java nio 
  • 大小: 23.2 KB
相关标签: nio