NIO的全称是non-blocking IO,也就是非阻塞IO,也有的人叫他New IO。他的核心内容主要有三部分,Channel(通道),Buffer(缓冲区), Selecto(选择器)。下面我们针对这三部分详细了解一下NIO。

Buffer(缓冲区)

Buffer缓冲是一个指定固定数据量的容器,一个连续数组。除内容之外,缓冲区还具有位置和界限,其中位置是要读写的下一个元素的索引,界限是第一个应该读写的元素的索引。java中每个非布尔基本类型都有一个缓冲区类。

Buffer通过capacity, position, limit, mark这四个变量来保存这个数据的当前位置状态,下面介绍一下这四个属性的意义。

  • capacity(容量值):缓冲区数组的总长度
  • position(位置):下一个要操作的数据元素的位置
  • limit(极限):缓冲区数组中不可操作的下一个元素的位置
  • mark(标记):用于记录当前position的位置,默认是-1

基本 Buffer 类定义了这些属性以及清除、反转和重绕方法,用以标记当前位置,以及将当前位置重置为前一个标记处。

  • clear()使缓冲区准备好信道读取或相对放置操作的一个新的序列:它设置了限制的能力和位置为零。

  • flip()使缓冲区准备好新的通道写入或相对获取操作序列:它将限制设置为当前位置,然后将位置设置为零。

  • rewind()使缓冲区准备好重新读取已经包含的数据:它保持限制不变,并将位置设置为零。

举例说明Buffer中各个属性:

public static void main(String[] args) {
        //创建一个10个字节的缓冲区,有两种方法
        byte[] bytes = new byte[10];
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        //ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        printBufferInfo(byteBuffer);
        //为缓冲区赋值
        for (int i = 0; i < 5; i++) {
            byteBuffer.put((byte) i);
        }
        System.out.println("初始赋值后的buffer信息为...");
        printBufferInfo(byteBuffer);

    }

    /**
     * 打印Buffer中的信息
     * @param byteBuffer
     */
    public static void printBufferInfo(ByteBuffer byteBuffer) {
        System.out.println("limit = " + byteBuffer.limit());
        System.out.println("capacity = " + byteBuffer.capacity());
        System.out.println("position = " + byteBuffer.position());
    }

打印结果如下:

limit = 10
capacity = 10
position = 0
初始赋值后的buffer信息为...
limit = 10
capacity = 10
position = 5

Channel(通道)

Channel是一个对象,可以通过它读取和写入数据。Channel和传统IO中的Stream很相似。主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如InputStream只能进行读取操作,OutputStream只能进行写操作;但是Channel中的所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

NIO中Channel的主要实现有:FileChannel(文件通道),DatagramChannel(UDP包通道),SocketChannel(socket客户端通道),ServerSocketChannel(socket服务端通道)。SocketChannel模拟连接导向的流协议(如TCP/IP),DatagramChannel则模拟包导向的无连接协议(如UDP/IP)。
Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。Channel和Buffer的关系如图:

Selecto(选择器)

Selector(选择器)提供了选择已经就绪的任务的能力。Selector会不断的轮询注册在上面的所有channel,如果某个channel为读写等事件做好准备,那么就处于就绪状态,对就绪状态的channel进行后续的IO操作。一个Selector能够同时轮询多个channel。这样,一个单独的线程就可以管理多个channel,从而管理多个网络连接。这样就不用为每一个连接都创建一个线程,同时也避免了多线程之间上下文切换导致的开销。
与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,主要有OP_ACCEPT(用于套接字准备接受操作位)、OP_CONNECT(用于套接字连接操作的操作位)、OP_READ(读操作的操作位)、OP_WRITE(写操作的操作位)。通过判断SelectionKey处于什么样的状态,进而做对应的操作。


为了避免篇幅太长,影响阅读体验,同时也是想对NIO一层一层的了解,一下灌入太多也不好,所以打算分几篇来介绍学习。上面说了一些概念的东西,下一篇将会结合代码来探究NIO。

版权声明:本文为wanghq1994原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/wanghq1994/p/12173062.html