Java NIO 概述

在 java 1.4 以前, Java 对 I/O 的支持并不完善,开发人员在开发高性能 I/O 程序的时候,会面临一些巨大的
挑战和困难,主要问题如下:

  • 没有数据缓冲区, I/O 性能存在问题;
  • 没有 C 或者 C++ 中的 Channel 概念,只有输入和输出流
  • 同步阻塞式 I/O 通信(BIO),通常会导致通信线程被长时间阻塞
  • 支持的字符集有限,硬件可移植性不好。

所以在 JDK 1.4 以后, Java 支持非阻塞的 I/O 通信模型的类库,被称为 NIO。可以理解为 New IO(新 I/O 模型),也可以理解成Not Blocking I/O(非阻塞 I/O 模型).

1、几个概念

在理解 BIO 与 NIO 之前,我们需要明白几个概念:

1.1 阻塞(Block)和非阻塞(Non-Block)

阻塞和非阻塞是进程在访问数据的时候,数据内是否准备就绪的一种处理方式,当数据没有准备的时候

阻塞:往往需要等待缓冲区中的数据准备好过后才处理其他的事情,否则一直等待在那里

非阻塞:当我们的进程访问我们的数据缓冲区的时候数据没有准备好的时候直接返回不需要等待.数据有的时候也直接返回.

1.2 同步(Synchronization)和异步(Asynchronous)

同步和异步都是基于应用程序和操作系统处理 IO 事件所采用的方式.比如同步应用程序要直接参与IO读写的操作,异步:所有的IO读写交给操作系统去处理.

同步:在处理IO事件的时候 必须阻塞在某个方法上面等待我们的IO事件完成(阻塞IO事件或则通过轮询 IO 事件的方式)

异步:所有的IO读写都交给了操作系统.这个时候我们可以去做其他的事情并不需要去完成真正的 IO 操作,当操作完成IO后给我们的应用程序一个通知就可以了。

2、NIO 与 IO

NIO 与 普通 IO 的主要区别如下:

IO NIO
面向流 (Stream oriented) 面向缓冲区(Buffer oriented)
阻塞式 IO (Blocking IO) 非阻塞式 (Non blocking IO)
多路复用器 (Selectors)

2.1 面向流与面向缓冲

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

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

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

2.2 阻塞与非阻塞IO

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

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

2.3 多路复用器(Selectors)

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

3、NIO 核心概念

Java Nio 由以下三个核心的概念组成:

  • Buffer : 缓冲区
  • Channel : 通道
  • Selector : 多路复用器

3.1 Buffer

缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。
在NIO中,所有的缓冲区类型都继承于抽象类Buffer

3.2 Channel

通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。

3.3 Selector

BIO 进行socket编程时,accept方法会一直阻塞,直到有客户端请求的到来,并返回socket进行相应的处理。整个过程是流水线的,处理完一个请求,才能去获取并处理后面的请求,当然也可以把获取socket和处理socket的过程分开,一个线程负责accept,一个线程池负责处理请求。

这里写图片描述

NIO 中非阻塞 I/O 采用了基于Reactor 模式的工作方式,I/O 调用不会被阻塞,相反是注册感兴趣的特定I/O事件,如可读数据到达,新的套接字连接等等,在发生特定事件时,系统再通知我们。NIO中实现非阻塞I/O的核心对象就是Selector,Selector就是注册各种I/O事件地方,而且当那些事件发生时,就是这个对象告诉我们所发生的事件。

这里写图片描述

当有读或写等任何注册的事件发生时,可以从Selector中获得相应的SelectionKey,同时从 SelectionKey中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据。

4、总结

希望通过以上的讲解大家能够理解 NIO 出现的原因以及 NIO 与 BIO 的区别,同时也能够理解 NIO 里面的核心概念。后续会具体分析 NIO 里面的核心概念达到深入理解 Java 里的 NIO。

参考文章:

并发编程网 – Java NIO 教程
Netty权威指南 – 李林锋

猜你喜欢

转载自blog.csdn.net/u012410733/article/details/80077704