NIO有三大核心组件:Selector、Buffer、Channel。
BIO中是基于流操作的,而NIO中是基于通道(channel)和缓冲区(buffer) 来操作的。
Selector(多路复用器)
一个selector可以注册进来多个channel,selector采用事件驱动的方式监听这些channel。
Selector常用的一些方法:
select():该方法会阻塞住,直到注册进selector的通道状态发生变化,才会放行;在放行之前,会将状态发生变化的channel的key放进一个set集合中。
selectedKeys():获取状态发生变化的通道。
selector.selectedKeys().clear():清除selectedKeys集合中的通道。
keys():获取所有的通道key对象。
Buffer
除了boolean类型没有对应的buffer,其他的都有。
在java NIO中缓冲区负责数据的存取,其内部就是一个数组,不同的缓冲区可以存储不同类型的数据。
Buffer中有四个核心属性:position、limit、capacity、mark。
position:buffer操作的所在位置,默认为0。
limit:buffer可操作的极限位置,默认等于capacity。
capacity:buffer存储数据的数组容量,在创建buffer的时候会指定。
mark:buffer的标记,一般这个值不会变,默认为-1,只有当调用mark()方法时,才会将position的值赋值给mark。
常用api:
flip():反转buffer的位置。
//源码分析
public final Buffer flip() {
//会将当前操作buffer的位置,改成buffer可操作的极限位置。
limit = position;
position = 0;
mark = -1;
return this;
}
allocate(int capacity):分配一个非直接缓冲区,将缓冲区建立在 JVM 的内存中。
allocateDirect(int capacity) 分配一个直接缓冲区,将缓冲区建立在物理内存中。可以提高效率。
array():返回当前buffer存储数据的数组。
clear():将四个属性都设置成初始值。
wrap():根据内容动态的返回一个和内容相同大小的buffer。
Channel
常用的通道有:
FileChannel: 专门操作文件IO的通道。
常用的方法:
read(ByteBuffer bytes):
从文件的当前位置(默认在0的位置)读取文件数据,一次读取ByteBuffer的长度个数据,然后使用实际读取的字节数更新该文件位置。可以查看源码可以知道,在调用通道调用read或者write的时候,都会先去判断postion和limit的位置,如果这两个值相等的话读取的一直是第一个字节。
所以如果有多个通道操作同一个buffer,另外一个通道操作完之后,又到第一个通道的时候,需要对buffer复原调用clear()方法。
read(ByteBuffer[] bytes):
和read(ByteBuffer bytes)方法类似,只不过这个方法可以同时将数据按顺序读取到多个buffer中。
register():将通道注册到指定的Selector,可以通过SelectionKey指定注册的模型。
configureBlocking():将通道在阻塞与非阻塞之间来回切换。
DatagramChannel: UDP协议的通道。
SocketChannel: socket客户端。
ServerSocketChannel: socket服务端。
三大组件之间的关系
1、一个Selector对应一个线程。
2、一个Selector对应多个Channel。
3、一个Channel对应一个Buffer。
注意:
1、ServerSocketChannel的职责只负责处理来自客户端段的连接,不做其他的操作。
2、SocketChannel负责处理服务器与客户端的读写操作。