面试官:谈谈你对IO流和NIO的了解

作者 : 开心源码 本文共4130个字,预计阅读时间需要11分钟 发布时间: 2022-05-12 共158人阅读

一、概念

NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。

二、NIO和IO的主要区别

下表总结了Java IO和NIO之间的主要区别:

1、面向流与面向缓冲

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

Java IO面向流意味着每次从流中读一个或者多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。假如需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。

Java NIO面向缓冲区的缓冲导向方法略有不同。数据读取到一个它稍后解决的缓冲区,需要时可在缓冲区中前后移动。这就添加了解决过程中的灵活性。但是,还需要检查能否该缓冲区中包含所有您需要解决的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未解决的数据。

2、阻塞与非阻塞IO

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

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

3、选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有可以解决的输入,或者者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

三、NIO和IO如何影响应用程序的设计

无论您选择IO或者NIO工具箱,可能会影响您应用程序设计的以下几个方面:

1.对NIO或者IO类的API调用。
2.数据解决。
3.用来解决数据的线程数。

1、API调用

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

2、数据解决

使用纯粹的NIO设计相较IO设计,数据解决也受到影响。

在IO设计中,我们从InputStream或者 Reader逐字节读取数据。假设你正在解决一基于行的文本数据流,例如:

该文本行的流可以这样解决:

 请注意解决状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道一定文本行就已读完, readline()阻塞直到整行读完,这就是起因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。 正如你可以看到,该解决程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已解决过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条准则:

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

注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据能否在缓冲区内。你所知道的是,该缓冲区包含少量字节,这使得解决有点困难。假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,“Name:An”,你能解决数据吗?显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何解决毫无意义。所以,你怎样知道能否该缓冲区包含足够的数据可以解决呢?好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必需检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如

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

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

假如缓冲区已满,它可以被解决。假如它不满,并且在你的实际案例中有意义,你或者许能解决其中的部分数据。但是许多情况下并非如此。

四、IO与NIO的选择

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

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

Java NIO: 单线程管理多个连接

假如你有一些的连接使用非常高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能非常契合。下图说明了一个典型的IO服务器设计:

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

PS.

1. Java IO 流中涉及到了哪些设计策略和设计模式

Java 的 IO 库提供了一种链接(Chaining)机制,可以将一个流解决器跟另一个流解决器首尾相接,以其中之一的输出作为另一个的输入而形成一个流管道链接,譬如常见的 new DataInputStream(new FileInputStream(file)) 就是把 FileInputStream 流当作 DataInputStream 流的管道链接。其次,对于 Java IO 流还涉及一种对称性的设计策略,其体现为输入输出对称性(如 InputStream 和 OutputStream 的字节输入输出操作,Reader 和 Writer 的字符输入输出操作)和字节字符的对称性(InputStream 和 Reader 的字节字符输入操作,OutputStream 和 Writer 的字节字符输出操作)。此外,对于 Java IO 流在整体设计上还涉及装饰者(Decorator)和适配器(Adapter)两种设计模式。

对于 IO 流涉及的装饰者设计模式例子如下:

//把InputStreamReader装饰成BufferedReader来成为具有缓冲能力的Reader。BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

对于 IO 流涉及的适配器设计模式例子如下:

//把FileInputStream文件字节流适配成InputStreamReader字符流来操作文件字符串。FileInputStream fileInput = new FileInputStream(file);InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

而对于上面涉及的两种设计模式浅显总结如下。装饰者模式就是给一个对象添加少量新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例(各种字符流间装饰,各种字节流间装饰)。适配器模式就是将某个类的接口转换成我们期望的另一个接口表示,目的是消除因为接口不匹配所造成的类的兼容性问题(字符流与字节流间互相适配)。

2. 字节流与字符流有什么区别:

计算机中的一切最终都是以二进制字节形式存在的,对于我们经常操作的字符串,在写入时其实都是先得到了其对应的字节,而后将字节写入到输出流,在读取时其实都是先读到的是字节,而后将字节直接使用或者者转换为字符给我们使用。因为对于字节和字符两种操作的需求比较广泛,所以 Java 专门提供了字符流与字节流相关IO类。对于程序运行的底层设施来说永远都只接受字节数据,所以当我们往设施写数据时无论是字节还是字符最终都是写的字节流。字符流是字节流的包装类,所以当我们将字符流向字节流转换时要注意编码问题(由于字符串转成字节数组的实质是转成该字符串的某种字节编码)。字符流和字节流的使用非常类似,但是实际上字节流的操作不会经过缓冲区(内存)而是直接操作文本本身的,而字符流的操作会先经过缓冲区(内存)而后通过缓冲区再操作文件。

字符流和字节流的使用非常类似,但是实际上字节流的操作不会经过缓冲区(内存)而是直接操作文本本身的,而字符流的操作会先经过缓冲区(内存)而后通过缓冲区再操作文件。

3. 字节流和字符流哪个好,如何选择?

缓大多数情况下使用字节流会更好,由于字节流是字符流的包装,而大多数时候 IO 操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的(图片等都是按字节存储的)。

而假如对于操作需要通过 IO 在内存中频繁解决字符串的情况使用字符流会好些,由于字符流具有缓冲区,提高了性能。


本文的重点是你有没有收获与成长,其他的都不重要,希望读者们能谨记这一点。同时我经过多年的收藏目前也算收集到了一套完整的学习资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货,希望对想成为架构师的朋友有肯定的参考和帮助

喜欢这篇文章的朋友可以点个喜欢,也可以关注一下我的个人专题:Java成长之路

需要更详细架构师技能思维导图和以下资料的可以加一下技术交流分享群:“708 701 457”免费获取




说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 面试官:谈谈你对IO流和NIO的了解

发表回复