NIO 的引入
在传统的 Java I/O 模型(BIO)中,I/O 操作是以阻塞的方式进行的。当一个线程执行一个 I/O 操作时,它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈,因为需要为每个连接创建一个线程,而线程的创建和切换都是有开销的。
为了解决这个问题,在 Java1.4 版本引入了 NIO
(New I/O or Non-Blocking I/O)java.nio
。提供了一种基于缓冲区、选择器和非阻塞 IO 模型的 IO 处理方式。相比于之前的 BIO
模型,NIO
可以实现更高的并发、更低的延迟以及更少的资源消耗。
I/O 包和 NIO 已经很好地集成了,java.io
也已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io
包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。
使用 NIO 并不一定意味着高性能,它的性能优势主要体现在高并发和高延迟的网络环境下。当连接数较少、并发程度较低或者网络传输速度较快时,NIO 的性能并不一定优于传统的 BIO 。
NIO 的组成部分
缓冲区(Buffer)
- 定义:缓冲区是 Java NIO 中的一个核心概念,它是用于存储IO操作数据的一段连续区域。
- 类型 :Java NIO 提供了多种类型的缓冲区,如
ByteBuffer
、CharBuffer
、IntBuffer
等,以适应不同的数据类型需求。 - 属性:每个缓冲区都有四个重要的属性,分别是容量(Capacity)、位置(Position)、限制(Limit)和标记(Mark)。
- 操作:缓冲区的读写操作会修改其位置和限制属性,以确保数据能够正确地进行传输和处理。
通道(Channel)
- 定义:通道是 Java NIO 中网络通信和文件 IO 操作的抽象,类似于传统 IO 中的 Stream,但它更加灵活和高效。
- 类型 :Java NIO 提供了多种类型的通道,如
FileChannel
(用于文件读写操作)、DatagramChannel
(用于 UDP 协议的网络通信)、SocketChannel
(用于 TCP 协议的网络通信)和ServerSocketChannel
(用于监听 TCP 连接请求)。 - 特点:通道可以与缓冲区结合使用,通过缓冲区直接在内存中进行数据的读写操作,从而避免了传统IO中的频繁磁盘访问。
选择器(Selector)
- 定义:选择器是Java NIO中的一个重要组件,它允许一个线程同时监听多个通道上的IO事件。
- 作用:通过选择器,可以实现单线程管理多个通道的方式,从而大大提高IO操作的并发性能。
- 使用:在选择器的模型中,每个通道都会注册到一个选择器上,并且每个通道都有一个其唯一的选择键(SelectionKey)对象来代表这个通道。当通道上的IO事件发生时,选择器会通知相应的线程进行处理。
NIO 与 BIO 的差异
I/O 与 NIO 最重要的区别是数据打包和传输 的方式,I/O 以流的方式处理数据,而 NIO 以块(缓冲区)的方式处理数据。
面向流的 I/O 一次处理一个字节数据: 一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。
面向块的 I/O 一次处理一个数据块,按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
传统 I/O 使用 Socket 和 SeverSocket 进行网络传输,存在阻塞问题。NIO 提供了 ServerChannel
和 ServerSocketChannel
,支持非阻塞网络传输,提高了并发处理能力。
BIO、NIO 与 AIO
BIO
(Blocking I/O):采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少且稳定的场景。
NIO
(New I/O 或 Non-blocking I/O):使用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过 Selector 监控多个 Channel 上的事件,提高性能和可伸缩性,适用于高并发场景。
AIO
(Asynchronous I/O):采用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,进一步提高了并发处理能力,适用于高吞吐量场景。
NIO 的优势
IO 操作主要在 文件IO 和 网络IO 场景下应用。NIO 的优势之处主要体现在网络 IO 场景下。
NIO(New I/O)的设计目标是解决传统 I/O(BIO,Blocking I/O)在处理大量并发连接时的性能瓶颈。传统 I/O 在网络通信中主要使用阻塞式 I/O,为每个连接分配一个线程。当连接数量增加时,系统性能将受到严重影响,线程资源成为关键瓶颈。而 NIO 可以在单个线程中处理多个并发连接 ,从而在网络传输中显著提高性能。
-
NIO 支持非阻塞 I/O,这意味着在执行 I/O 操作时,线程不会被阻塞。这使得在网络传输中可以有效地管理大量并发连接(数千甚至数百万)。而在操作文件时,这个优势没有那么明显,因为文件读写通常不涉及大量并发操作。
-
NIO 支持 I/O 多路复用 ,这意味着一个线程可以同时监视多个通道(如 Socket),并在 I/O 事件(如可读、可写)准备好时处理它们。这大大提高了网络传输中的性能,因为单个线程可以高效地管理多个并发连接。操作文件时这个优势也无法提现出来。
-
NIO 提供了
ByteBuffer
,可以高效地管理缓冲区。这在网络传输中很重要,因为数据通常是以字节流的形式传输。操作文件的时候,虽然也有缓冲区,但优势仍然不够明显。