NIO概览
Java NIO API的主要抽象由以下这些构成:
. Buffer
-- 用来存储数据的
. Charsets 以及与此相关的decoder和encoders
-- 用于Unicode字符和字节间的转换
. 各种类型的Channels
-- Channel是用于表示连接。这些连接指向的能够执行IO操作的实体。
. Selectors & selection keys
-- selector、 selectionn keys 以及 selectable channel 他们共同实现了非阻塞多路复用IO的功能。
buffer包
Java.nio.buffer包下提供了一些buffer类,这些buffer类会一直贯穿于NIO API的使用过程中。在java.nio.charset包下定义了charset的API,java.nio.channels包下定义了channel和selector的API。这些包的任意一个自包都有其自己的SPI包,这些包可用于扩展平台的默认实现。
buffer | 相关描述 |
---|---|
Buffer | Position, limit, and capacity;clear,flip,rewind,mark/reset |
ByteBuffer | Get/put, compact,views ,allocate,wrap |
MappedByteBuffer | A byte buffer mapped to a file |
CharBuffer | Get/put,compact,allocate,wrap |
DoubleBuffer | ''' |
FloatBuffer | '' |
intBuffer | '' |
LongBuffer | '' |
ShortBuffer | '' |
ByteOrder | 用于字节排序的类安全枚举 |
一个buffer就是用于存储某个基本数据类型数据的固定大小的容器。除了它存储的内容之外,一个buffer通常还有以下几个属性:
position —— 读取和写入操作的下一个元素的角标
limit —— 首个不能读或写的元素角标
基础类Buffer除了定义了这些属性之外,还提供了以下几个方法:
clearing
flipping
rewinding
marking
resetting
对于每一个非布尔的基本数据类型,都有一个相关的buffer类。每个类都定义了get/put方法以用于移出或移入数据,用于compacting ,duplicating,slicing 方法,以及用于分配一个新buffer和把某个现存数组包裹进一个buffer中的静态方法。
字节buffer和他们的区别之处在于,字节数组可以作为IO操作的数据源和目标对象。除此之外,他们还支持:
. 字节buffer可以在直接内存上完成分配,此时的JVM虚拟机会直接使用直接内存执行native I/O操作,从而在某些场景下显著地提高性能,避免了在Java堆和Native堆中来回复制数据。
. 字节buffer可以把一个文件区域映射到内存上,此时就可以使用定义在 MappedByteBuffer类中的某些和文件相关的操作
. 字节buffer以大端字节序或小端字节序的形式提供对任何非布尔基元类型的二进制数据的异构或同质序列的访问权限
channels包
该包下定义了channels相关的类,channel代表了程序与可执行IO操作的实体之间的连接,这些实体可以是文件、套接字等。同时,该包下还定义了selectors,用以实现多路复用的非阻塞IO操作。
channel | 相关描述 |
---|---|
Channel | Channel表示了I/O操作的连接 |
ReadableByteChannel | 可以读进到一个缓冲区中 |
ScatteringByteChannel | 可以读进一系列buffer中 |
WritableByteChannel | 可以从一个buffer写入 |
GatheringByteChannel | 可以从一系列buffer中写入 |
ByteChannel | 即可读又可写 |
SeekableByteChannel | 和一个包含变长字节的实体之间的ByteChannel |
AsynchronousChannel | 支持异步IO操作 |
AsynchronousByteChannel | 可以异步地读取和写入字节 |
NetworkChannel | 连接到一个网络套接字的Channel |
MulticastChannel | 可以加入IP多播组 |
Channels | channel/stream间操作的工具方法 |
channel用以表示与实体之间的连接,这些实体像:硬件设备、文件、网络套接字、或者一个能够执行IO操作的程序组件。正如Channel接口中所规定的那样,channel要么是关闭的,要么是打开的,并且都是异步可关闭、可中断的。
除了Channel接口之外,其他的几个接口也扩展了Channel接口。ReadableByteChannel接口定义了一个read方法,该read方法可以把将channel中的字节读取到buffer中;类似地,WritableByteChannel接口定义了一个write方法,此write方法可以将一个buffer中的字节读取到channel中。ByteChannel接口则综合了这俩个接口,它不仅可以读而且可以实现写操作。SeekableByteChannel接口则扩展了ByteChannel接口,增加了query和modify方法。
ScatteringByteChannel和GatheringByteChannel接口扩展了ReadableByteChannel以及WritableByteChannel接口,各自分别新增了 read 和write方法,这些方法可以接收一系列的buffer而不是单一的buffer。
NetworkChannel接口定义了一些方法可以绑定channel的套接字、获取该套接字所绑定的地址,以及和socket相关的get/set操作。而MulticastChannel接口则定义了一些可以接入ip多播组的方法。
Channels工具类中定义了一些支持流间操作的静态方法,由InputStream和OutputStream可以构造出相应channel,相反地,由channel也能构造出InputStream和OutputStream。从一个给定的可读字节channel中可以构造出Reader,相应地,使用一个给定的字符集来把字符编码成字节,并把他们写入到给定的可写字节通道中。
File channles | 相关描述 |
---|---|
FileChannel | 读、写、映射、操作文件 |
FileLock | 文件上的锁 |
FileChannel | 映射了文件区域到一个直接内存缓存区 |
FileChannel类支持向某个文件相关的channel中读字节和写字节操作,以及查询和修改当前文件的位置、把文件截断到给定特定大小。另外,它还定义了一些方法获取整个文件的锁或某个文件特定区域的锁,这些方法都会返回FileLock类。最后,它还定义了方法可以将文件的更新操作写回到存储设备。
调用FileChannel的静态方法open,或者调用FileInputStream、FileOutputStream、RandomAccessFile的getChannel方法都可以创建一个FileChannel。
多路复用非阻塞IO | 描述 |
---|---|
selectableChannel | 读、写、映射、操作文件 |
DatagramChannel | 通向面向数据包套接字的channel |
Pipe.SinkChannel | 管道的写端 |
Pipe.SourceChannel | 管道的读端 |
ServerSocketChannel | 通向面向流的监听套接字的channel |
SocketChannel | 通向面向流的连接套接字的channel |
Selector | 可选通道的多路复用器 |
SelectionKey | channel注册到selector上之后返回的令牌标识 |
Pipe | 组成一个单向管道的俩个channel |
相较于面向线程的阻塞I/O来说,多路复用非阻塞IO更加可扩展,多路复用非阻塞IO由selectors,selectable channels以及selection keys共同组成。
selector是可选通道的一个多路复用器,反过来它也是一个可被置于non-blocking模式的特殊通道。要执行多路复用IO操作,首先要创建多个可选通道,并被置于非阻塞模式,并且都注册到一个selector上。注册通道规定了一组IO操作,selector将检查这些IO操作是否已就绪,并返回一个用于表示此注册的selection key。
一旦某些通道被注册到selector上,它就会执行选择操作,用以发现先前声明的操作中哪个通道是处于就绪状态的。如果某个通道处于就绪状态,那么注册操作返回的key就会被添加到selector的selected-key集合中。这个key集合以及其中的key,可以用于检测通道的哪个操作处于就绪状态。
使用这个key,用户可以获取执行IO操作的相应的通道。
参考文献
- Oracle文档
- Which memory is faster Heap or ByteBuffer or Direct ?
- Direct vs Non-direct buffer
- channels包
- 美团Java NIO浅析
- jenkov NIO博客