1. Nio 简介

1.1. NIO 概述


Java NIO(New IO)是从 Java 1.4 版本开始引入的一组新的 IO API(核心构成有 Channels,Buffers,Selectors三部分),目的主要是基于这组 API 改善 IO操作性能。

1.2. NIO&IO 分析

  • 1.2.1. IO 操作流程


对于一个 network IO (这里我们以 read 举例),它会涉及到两个系统对象,一个是调用这个 IO 的 process (or thread),另一个就是系统内核(kernel)。当一个 read 操作发生时,该操作会经历两个阶段:
1) 将数据拷贝到操作系统内核 Buffer。
2) 将操作系统内核数据拷贝到用户进程。

  • 1.2.2. 面向流与面向缓冲区


Java NIO 和 IO 之间第一个最大的区别是:IO 是面向流的,NIO 是面向缓冲区的。例如:
1) 面向流的操作(输入&输出流操作是单向的)

1. Nio 简介_第1张图片

2)面向缓冲区的操作:(操作是双向且可移动指针

1. Nio 简介_第2张图片

说明:
面向缓冲区的操作时,是缓冲区中的读写模式进行操作,写模式用于向缓冲区写数据,读模式用于从缓冲区读。

 

  • 1.2.3. 阻塞与非阻塞


阻塞和非阻塞的概念描述的是用户线程调用内核 IO 操作的方式。

1) 阻塞:是指调用操作需要等待结果的完成,同时会影响后操作的执行。例如阻塞式 IO 操作,用户线程会在内核等待数据以及拷贝数据期间都处于阻塞状态。

1. Nio 简介_第3张图片

整个 IO 请求的过程中,用户线程是被阻塞的,这导致用户在发起 IO 请求时,不能做任何事情,对 CPU 的资源利用率不够。

场景比喻:小明去火车站买票,火车站没票,小明在车站一直等待出票,过了两天才买到一张票。

这里:小明就是阻塞的, 要一直等在火车站,直到火车站有人退票才有票可买,这期间,小明就一直等着了。

 

2) 非阻塞:是指 IO 操作被调用后立即返回给用户一个状态值,无需等到 IO 操作彻底完成。例如:

1. Nio 简介_第4张图片

虽然用户线程每次发起 IO 请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的 CPU 的资源。

场景比喻:小明去火车站买票,火车站没票,然后每隔 3 小时去小明去一次火车站问有没有人退票,两天天后买到一张票

这里:由于不断地每个三个小时小明要去车站问问,对于如果有多个客户需要买票,他们都每三个小时都去车站问问,这会消耗很多的资源和空间。对于单个客户来说也是会浪费每次过去查询的时间和资源。

 

为了避免同步非阻塞 IO 模型中轮询等待的问题,基于内核提供的多路分离函数 select(),可以实现 IO 的多路复用,例如

1. Nio 简介_第5张图片

使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的 IO 请求。用户可以注册多个 socket,然后不断地调用 select 读取被激活的socket,即可达到在同一个线程内同时处理多个 IO 请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

场景比喻:小明去想要买票,委托黄牛,然后每隔 1 小时电话问问黄牛,黄牛两天内买到票,然后小明去火车站交钱领票。

这里:黄牛是可以同时代为购买多个人的票的,而小明就不用一直在车站等着结果,从而省下来时间继续做其他的事情。所以,这里的小明就不是阻塞的了,但是这里的黄牛还是阻塞的,他是一直在车站等着的。

这里的 select 函数是阻塞的,因此多路 IO 复用模型也被称为异步阻塞 IO模型。注意,这里的所说的阻塞是指 select 函数执行时线程被阻塞,而不是指socket。

  • 1.2.4. 同步与异步


同步和异步的概念描述的是用户线程与内核的交互方式。
1) 同步是指用户线程发起 IO 请求后需要等待或者轮询内核 IO 操作完成后才能继续执行。

1. Nio 简介_第6张图片

说明:这种用户线程轮询的改进方式是 IO 多路复用的实现。
2) 异步是指用户线程发起 IO 请求后仍继续执行,当内核 IO 操作完成后会通知用户线程,或者调用用户线程注册的回调函数,例如

1. Nio 简介_第7张图片

说明:
异步 IO 的实现需要操作系统的支持,目前系统对异步 IO 的支持并非特别完善,更多的是采用 IO 多路复用模型模拟异步 IO 的方式。

场景比喻:小明去火车站买票,给售票员留下电话,有票后,售票员电话通知小明并快递送票上门

你可能感兴趣的:(NIO)