JAVA-NIO(一)

概述

NIO是在JDK1.4中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,这是原来的 I/O 包所无法做到的。在原来的java编程中,使用的IO模式是流的方式,所有的IO都是以单个字节的方式移动的,通过一个Stream对象进行移动。而NIO则以块的方式处理IO,这种处理方式更加符合操作系统的IO处理方式(程序语言对与IO的处理都是借助于操作系统的IO系统进行处理的)
NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。操作系统与 Java 基于流的 I/O模型有些不匹配。操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取( DMA)的协助下完成的。而 JVM 的 I/O 类喜欢操作小块数据——单个字节、几行文本。结果,操作系统送来整缓冲区的数据, java.io 的流数据类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象。操作系统喜欢整卡车地运来数据, java.io 类则喜欢一铲子一铲子地加工数据。有了 NIO,就可以轻松地把一卡车数据备份到您能直接使用的地方( ByteBuffer 对象)。

通道与缓冲区

通道与缓冲区是NIO的核心内容,几乎每一步IO操作都会需要它。

通道

通道是进行输入输出时的一个数据流流通的载体,进行文件数据的读取时,需要打开一个通道,通过该通道进行数据的读取,进行socket通信时,读取数据时也需要一个通道,针对不同的数据源,NIO提供了不同的通道。
通道是双向的,可以同时进行读写。

缓冲区

NIO的缓冲区本质是一个Buffer对象,底层是一个数组。用来存放从管道读取的数据,或者将数据输出到管道。在NIO中,所有的数据都是用缓冲区进行处理的,在读取数据时,是直接读取到缓冲区,将要输出的数据也是先写入到缓冲区。
NIO的缓存区分为一些几种类型:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
    每一个Buffer类都是Buffer子类。

缓冲区的结构

对于Buffer具有两个重要的组件:状态变量与访问方法。

状态变量

状态变量是缓冲区对内部数据的一种跟踪方法,由于每一步进行读写都会改变缓冲区的状态,缓冲区通过状态变量管理自己的资源,这样调用方就无需关心内部结构。

Buffer具有三个指定任意时刻缓冲区的状态的变量:

  • position
  • limit
  • capacity

position

它指定了下一个字节将要放到数组的那个位置,比如说如果从通道读取了三个字节,那么position将会被设置为3,指向数组的第四个位置。

limit

表示还有多少数据需要取出(从缓冲区写入通道时),或者还有多少空间可以存放数据(从通道写入缓冲区时),position<=limit

capacity

表示了缓冲区的最大容量,其实就是数组的大小。limit决不能大于capacity

模拟读写时,状态变量的变化

我们首先观察一个新创建的缓冲区。出于本例子的需要,我们假设这个缓冲区的 总容量 为8个字节。 Buffer 的状态如下所示:
这里写图片描述
回想一下 ,limit 决不能大于 capacity,此例中这两个值都被设置为 8。我们通过将它们指向数组的尾部之后(如果有第8个槽,则是第8个槽所在的位置)来说明这点。
JAVA-NIO(一)_第1张图片
position 设置为0。如果我们读一些数据到缓冲区中,那么下一个读取的数据就进入 slot 0 。如果我们从缓冲区写一些数据,从缓冲区读取的下一个字节就来自 slot 0 。 position 设置如下所示:
JAVA-NIO(一)_第2张图片
由于 capacity 不会改变,所以我们在下面的讨论中可以忽略它。
第一次读取
现在我们可以开始在新创建的缓冲区上进行读/写操作。首先从输入通道中读一些数据到缓冲区中。第一次读取得到三个字节。它们被放到数组中从 position 开始的位置,这时 position 被设置为 0。读完之后,position 就增加到 3,如下所示:
JAVA-NIO(一)_第3张图片
limit 没有改变。
第二次读取
在第二次读取时,我们从输入通道读取另外两个字节到缓冲区中。这两个字节储存在由 position 所指定的位置上, position 因而增加 2:
JAVA-NIO(一)_第4张图片
limit 没有改变。
flip
现在我们要将数据写到输出通道中。在这之前,我们必须调用 flip() 方法。这个方法做两件非常重要的事:
它将 limit 设置为当前 position。
它将 position 设置为 0。
前一小节中的图显示了在 flip 之前缓冲区的情况。下面是在 flip 之后的缓冲区:
JAVA-NIO(一)_第5张图片
我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。
第一次写入
在第一次写入时,我们从缓冲区中取四个字节并将它们写入输出通道。这使得 position 增加到 4,而 limit 不变,如下所示:
JAVA-NIO(一)_第6张图片
第二次写入
我们只剩下一个字节可写了。 limit在我们调用 flip() 时被设置为 5,并且 position 不能超过 limit。所以最后一次写入操作从缓冲区取出一个字节并将它写入输出通道。这使得 position 增加到 5,并保持 limit 不变,如下所示:
JAVA-NIO(一)_第7张图片
clear
最后一步是调用缓冲区的 clear() 方法。这个方法重设缓冲区以便接收更多的字节。 Clear 做两种非常重要的事情:
1. 它将 limit 设置为与 capacity 相同。
2. 它设置 position 为 0。
下图显示了在调用 clear() 后缓冲区的状态:
JAVA-NIO(一)_第8张图片
缓冲区现在可以接收新的数据了。

上述有提到几个方法:flip()、clear(),其实就是改变position、capacity、limit的值
看源码很清晰:

//clear 方法
public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

//flip 方法
public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

// rewind 方法
 public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

访问方法

访问方法是操作缓冲区中的数据,可以进行读取,写入。如前面所说,其底层是一个数组,所以可以进行指定位置的读取与写入。这是流无法带来的。

链接:一个讲NIO清晰到不能再清晰的文章
本文是楼主对链接文章学习后的一个高仿文章,由于原文太长,所以进行了切割。

你可能感兴趣的:(Java)