资讯专栏INFORMATION COLUMN

什么时候使用NIO?

Taste / 446人阅读

摘要:一前言学习了和之后,肯定会问我们到底什么时候该使用,什么时候该使用在下文中我会尝试用例子阐述和的区别,以及它们对你的设计会有什么影响。

一、前言

学习了Java IO 和 NIO之后,肯定会问:我们到底什么时候该使用 IO,什么时候该使用 NIO?
在下文中我会尝试用例子阐述java NIO 和IO的区别,以及它们对你的设计会有什么影响。

二、NIO和IO的主要区别
IO NIO
面向流(Stream) 面向缓冲(Buffer)
阻塞IO 非阻塞IO
选择器(Selectors)
2.1 面向流与面向缓冲

Java NIO 和 IO 之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。

Java IO 面向流意味着每次从流中读一个或多个字节,直至读取所有字节,只能顺序读取所有数据。如果想要跳过一些字节或者想要读取已经读取过的数据,则必须将从流中的数据线缓存起来。

Java NIO的处理方式不一样。数据一开始就被读写到缓冲区(Buffer),根据需要你可以控制读取什么位置的数据。这就增加了处理过程中的灵活性。然而,你需要额外做的工作是检查你需要的数据是否已经全部到了Buffer中,你还需要保证当有更多的数据进入Buffer中时,Buffer中未处理的数据不会被覆盖

2.2 阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。

Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个多带带的线程现在可以管理多个输入和输出通道(channel)。

2.3 Selectors

Java NIO 的 Selectors 允许一条线程去监控多个 channels 的输入,你可以向一个 selector 上注册多个channel,然后调用 selector 的 select() 方法判断是否有新的连接进来或者已经在 selector 上注册时channel 是否有数据进入。selector 的机制让一个线程管理多个 channel变得简单。

三、NIO和IO对应用的设计有何影响

选择使用NIO还是IO做你的IO工具对应用主要有以下几个方面的影响
1、使用IO和NIO的API是不同的(废话)
2、处理数据的方式
3、处理数据所用到的线程数

3.1 API调用

当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。

3.2 处理数据的方式

在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(); 

注意,一旦reader.readLine()方法返回,你就可以确定整行已经被读取,readLine()阻塞知道一整行都被读取

NIO的实现会有一些不同,下面是一个简单的例子

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

注意第二行从channel中读取数据到ByteBuffer,当这个方法返回你不知道是否你需要的所有数据都被读到buffer了,你所知道的一切就是有一些数据被读到了buffer中,但是你并不知道具体有多少数据,这使程序的处理变得稍微有些困难

想象一下,调用了read(buffer)方法后,只有半行数据被读进了buffer,例如:“Name: An”,你能现在就处理数据吗?当然不能。你需要等待直到至少一整行数据被读到buffer中,在这之前确保程序不要处理buffer中的数据

你如何知道buffer中是否有足够的数据可以被处理呢?你不知道,唯一的方法就是检查buffer中的数据。可能你会进行几次无效的检查(检查了几次数据都不够进行处理),这会令程序设计变得比较混乱复杂

ByteBuffer buffer = ByteBuffer.allocate(48);  
  
int bytesRead = inChannel.read(buffer);  
  
while(! bufferFull(bytesRead) ) {  
    bytesRead = inChannel.read(buffer);  
}  

bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。

bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。

如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了“缓冲区数据循环就绪”:

3.3 用来处理数据的线程数

NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。

如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。同样,如果你需要维持许多打开的连接到其他计算机上,如P2P网络中,使用一个多带带的线程来管理你所有出站连接,可能是一个优势。一个线程多个连接的设计方案如下图所示:

如果你只有少量的连接但是每个连接都占有很高的带宽,同时发送很多数据,传统的IO会更适合。下图说明了一个典型的IO服务器设计:

Java IO: 一个典型的IO服务器设计- 一个连接通过一个线程处理.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76346.html

相关文章

  • 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0
  • JDK10都发布了,nio你了解多少?

    摘要:而我们现在都已经发布了,的都不知道,这有点说不过去了。而对一个的读写也会有响应的描述符,称为文件描述符,描述符就是一个数字,指向内核中的一个结构体文件路径,数据区等一些属性。 前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 本来我预想是先来回顾一下传统的IO模式的,将传统的IO模式的相关类理清楚(因为IO的类很多)。 但是,发现在整理的过程已...

    YFan 评论0 收藏0
  • 一文理解:Java NIO 核心组件

    摘要:的出现解决了这尴尬的问题,非阻塞模式下,通过,我们的线程只为已就绪的通道工作,不用盲目的重试了。注意要将注册到,首先需要将设置为非阻塞模式,否则会抛异常。 showImg(https://segmentfault.com/img/remote/1460000017053374); 背景知识 同步、异步、阻塞、非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下。 ...

    Coding01 评论0 收藏0
  • 一文让你彻底理解 Java NIO 核心组件

    摘要:的出现解决了这尴尬的问题,非阻塞模式下,通过,我们的线程只为已就绪的通道工作,不用盲目的重试了。注意要将注册到,首先需要将设置为非阻塞模式,否则会抛异常。 同步、异步、阻塞、非阻塞首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下[1]。 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节)。 异步:相对于同步,API调用返回时调用者不知道操作...

    guyan0319 评论0 收藏0
  • 好记性不如烂笔头——NIO学习笔记

    摘要:该线程在此期间不能再干任何事情了。线程通讯线程之间通过等方式通讯。选择器传统的模式会基于服务器会为每个客户端请求建立一个线程由该线程单独负贵处理一个客户请求。 本文是对NIO知识的归纳与整理 1.阻塞与同步 1)阻塞(Block)和非租塞(NonBlock): 阻塞和非阻塞是进程在访问数据的时候,数据是否准备就绪的一种处理方式,当数据没有准备的时候阻塞:往往需要等待缞冲区中的数据准备好...

    王伟廷 评论0 收藏0

发表评论

0条评论

Taste

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<