Java NIO

在 Java 中,一共有三种 I/O 模型:BIO、NIO、AIO,关于它们的简述可见 Java 中三种常见的 I/O 模型。本篇文章主要介绍 Java NIO 的相关内容~~

Java NIO 简述

本着完整的原则,在文章开头简单介绍一下 Java NIO 的核心思想~~

Java NIO 是非阻塞的 I/O 多路复用 模型,服务端实现了一个线程管理多个客户端连接,这些连接都会注册到多路复用器 Selector 中,Selector 会轮询所管理的连接,当连接有事件发生就去处理它

在 Java BIO 中,一个线程负责一个连接,当连接没有可读可写请求时线程会阻塞,十分浪费线程资源,而 Java NIO 中一个线程管理多个连接,只要有一个连接有 I/O 事件发生就可以去处理它

Java NIO 有三大核心组件:Buffer (缓冲区)Channel (通道)Selector (多路复用选择器),如下图所示:

9

Java NIO 中所有的 I/O 请求都是从 Channel 开始:

上面三大核心组件有如下关系:

Java BIO 和 Java NIO 的区别:

Buffer

下面介绍 Java NIO 中第一个核心组件 Bufer (缓冲区),它底层是一个数组。Java 中Buffer是一个抽象类,有七个子类,除 boolean 类型的其它基本数据类型都对应一个Buffer的子类

2

Buffer的每个子类中,都有一个对应类型的数组,这也是说 Buffer 缓冲区底层是一个数组的原因,以IntBuffer为例:

下面主要介绍为什么 Buffer 既可以读也可以写,这是如何实现的!!在Buffer类中,有四个属性:

与此同时,还有一个flip()方法用于读写切换,它会改变上面四个属性:

下面模拟一下 Buffer 读、写、读写切换的过程。假设起初有一个容量为 5 的 Buffer,那么 position = 0,limit = capacity = 5,如下图所示:

3

如果向 Buffer 中添加了三个数0, 1, 2,此时 position = 3。假设现在开始读数据,先调用flip()方法,那么 position = 0,limit = 3,capacity = 5,如下图所示:

4

从上面例子可知,当为写模式时,只能在[0, limit)区间内写数据;当为读模式时,只能在[0, limit)区间内读数据

下面给出上面例子对应的代码实现:

Channel

下面介绍 Java NIO 中第二个核心组件 Channel (通道),它是双向的,既可以从里面读数据,也可以往里面写数据,而且所有的数据交互都需要用到 Buffer 和 Channel

假设客户端向服务器发送一条消息,那么整个流程为:

可能描述比较抽象,直接上图:

5

从上图可以看出两个方法;

技巧:这两个方法一开始很难分清到底是往谁写、往谁读,其实只要把 Channel 作为动作的发出者即可,write()就是往 Channel 中写,read()就是从 Channel 中读,而交互的目标都是 Buffer

介绍了 Channel 在 Java NIO 中的作用后,下面来看看到底有哪几种具体的 Channel:

6

上面介绍过,客户端和服务器两边其实都有 Channel,ServerSocketChannel 面向于服务器,主要负责处理连接请求,当有一个新连接时,ServerSocketChannel 会监听到该请求,然后创建一个 SocketChannel 负责后续和该连接进行读写操作,文末给出的 Demo 会更加清楚阐述这一区别

Selector

下面介绍 Java NIO 中第三个核心组件 Selector (多路复用器),它的作用和 I/O 多路复用 中的select()相同,一个线程可以管理多个 Channel (注意:Channel 和连接等价)

下面简述一下 Selector 的原理:

Selector 其实是底层是一个Set<SelectionKey>集合,每个 SelectionKey 都对应一个 Channel,可以通过 SelectionKey 获取对应的 Channel

Demo

下面给出一个 Java NIO 的 Demo ~

先给出服务端的代码:

再给出客户端的代码:

参考文章