NIO(no-blocking I/O)
在oracle的官方文档中有明确的指出,java.io 和 java.nio 提供了丰富的API去管理一个应用的 I/O。功能包括 文件、设备的 I/O、对象的序列化、缓冲区管理、字符集的支持等等。同时最主要的NIO的特色就是在于:多路复用、非阻塞式IO、内存映射、文件锁。更多细节建议大家也可看官方文档I/O documentation
接下来就从不同的方面来介绍NIO
I/O 流
也可称之为数据序列 (A stream is a sequence of data)
I/O ? 或者输入/输出 ? 一个 I/O 流代表的是一个输入源和输出对象。流可以代表不同类型的源和目标。包括磁盘文件、设备、其他程序和内存数组等。流支持多种不同类型的数据,包括简单字节、原始数据类型、本地化字符和对象。一些流只是简单的用于传输数据。无论它们在内部如何工作,所有流都向使用它们的程序呈现相同的简单模型。也就是简单的读取和输入数据(如下图)。
为什么要使用NIO?
NIO 与原来 IO 有相同的作用和目的,但是它使用的方式不同:采用的是块 I/O。NIO创建的目的就是为了让Java程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而极大地提高速度。
流与块的比较
面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。
一个面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
NIO中的核心概念
通道、缓冲区、选择器
通道和缓冲区是 NIO 的核心对象。简单来说,通道就是模拟流而缓冲区本质上就是数组,但是它又只是数组。就是存储要在通道中传输的数据(包含输入流读取、输出流写入),类似于一个容器。
缓冲区
Buffer 是一个对象,它包含了写入或者刚读出的数据。在 NIO 中加入 Buffer 对象也是为了与原 I/O的一个重要区别。在 I/O 中,会将数据直接操作到 Stream对象中。
在 NIO 体系中,任何时候数据都是通过缓冲区进行处理的,包括读取数据、写入数据。
每一种Java基本类型都有对应的缓冲区
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
与数组不同的地方
通常情况下,缓冲区是一个字节数组,但是也可以使用其他类型的数组
与普通数组不同的地方在于,缓冲区提供了对数据的结构化访问,是可以跟踪当前程序的读/写进度
缓冲区内部维护了几个不同的私有状态变量来跟踪读/写进度
// Invariants: mark <= position <= limit <= capacity
private int mark = -1; // 表示当前缓冲区的标记
private int position = 0; // 当前读取/写入的位置
private int limit; // 当前读取/写入的数据最大限制
private int capacity; // 数组的最大容量
一个简单的ByteBuffer的声明
ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 通过allocate return new HeapByteBuffer(capacity, capacity);
ByteBuffer byteBufferWrap = ByteBuffer.wrap(new byte[1024]); // 通过wrap 包装数组 return new HeapByteBuffer(array, offset, length);