一、Java NIO 基本介绍

  1. Java NIO 全称 Java non-blocking IO ,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 NewIO),是同步非阻塞的。
  2. NIO 相关类都被放在 java.nio 包及子包下,并且对原 java.io 包中的很多类进行改写。
  3. NIO 有三大核心部分: Channel(通道)、Buffer(缓冲区)、Selector(选择器)
  4. NIO面向缓冲区,或者面向块编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络。
  5. Java NIO 的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
  6. 通俗理解:NIO 是可以做到用一个线程来处理多个操作的。假设有 10000 个请求过来,根据实际情况,可以分配 50 或者 100 个线程来处理。不像之前的阻塞 IO 那样,非得分配 10000 个。

文章开头.jpg

二、 NIO 和 BIO 的比较

  1. BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多。
  2. BIO 是阻塞的,NIO 则是非阻塞的。
  3. BIO 基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。


三、 NIO 的流向说明


**理解:数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。其实就是流向的问题,由下举例解释

  1. 客户端服务端传输数据 站在服务端(程序)的角度看 ,数据是到程序里面来的所以是

    1. **InputStream 对应的是 read , 过程是: 客户端数据===>通道===>然后把通道数据读取到缓冲区中===>我们从缓冲区中拿到数据进行程序设计**
  2. 服务端(程序)给客户端(目标人物)传输数据,站在服务器(程序)的角度看.数据是从服务器出去的所以是

OutputStream 对应的是 write,过程是: 服务端数据===>把数据放在缓冲区中===>缓冲区数据写入到通道中===>客户端拿到数据进行业务处理

四、Selector、Channel 和 Buffer 关系图(简单版)

NIO 简介(一) - 图2

NIO的三大核心 : Selector、Channel 和 Buffer
图介绍:

  1. 每个 Channel 都会对应一个 Buffer
  2. Selector 对应一个线程,一个线程对应多个 Channel(连接)。
  3. 该图反应了有三个 Channel 注册到该 Selector //程序
  4. 程序切换到哪个 Channel 是由事件决定的,Event 就是一个重要的概念。
  5. Selector 会根据不同的事件,在各个通道上切换。
  6. Buffer 就是一个内存块,底层是有一个数组。
  7. 数据的读取写入是通过 Buffer,这个和 BIOBIO 中要么是输入流,或者是输出流,不能双向,但是 NIOBuffer 是可以读也可以写,需要 flip 方法切换 Channel 是双向的,可以返回底层操作系统的情况,比如 Linux,底层的操作系统通道就是双向的。

    五、 通俗讲述

    原本以前的BIO 一个连接也就是得开辟出来一个线程进行读取,并且BIO中要么是输入流,要么是输出流,不能双向进行,这样无限制的开新线程处理连接,就算连接中什么事都没有干(读写),他这个线程还会在哪占用着资源。在高并发,高可用的应用场景中,这无疑是一种致命的操作,线程那么宝贵的资源,被无情浪费,就算用线程池进行管理线程也是亡羊补牢,没有什么大的实际解决。
    经过分析了BIO的问题我总结3点:
    1.线程、连接一对一
    2.阻塞io
    3.读取写入要么输入要么输出
    NIO就是针对这3点问题进行完善,使用3个核心组件一一攻破,首先不再是一个线程对应一个连接,而是一个线程对应一个selector,然后一个selector可以被多个channel(连接)注册,通过Event(事件)轮询进行切换程序去处理哪一个clannel(连接),这样就把BIO的第一个第二个问题解决了。然后又引申出一个buffer概念,每一个clannel(连接)对应他们自己的buffer,把流处理接换成面向buffer或者说面向块处理,然后这样不仅解决了缓冲作用给性能又带来了优化,而且buffer还是双向的,通过buffer的flip方法反转一下buffer,就可以轻松解决读写的问题,不再像BIO那样读就是读、写就是写。