对于如今的数据时代,响应速度不仅取决于服务器的硬件,对于服务的设计实现更是重中之重。所以对资源的利用率便城市代码实现的一大考量点。这就更加体现出的NIO在实际开发中的重要性。随着框架的封装度日益提高,人们更加注重与对它的实际使用,但是对于底层的了解才是处理解决生产问题的正确路径。
NIO的核心分为三块
- Channel(通道)
- Buffer(缓冲区)
- Selector(选择器)
(1)Channel
它是从节点获取数据的工具,与Stream流有类似之处。但是流是单向流通的,而通道是双向的。
通道的类别也有很多
- FileChannel 文件管道的数据
- Pipe.SourceChannel 线程间通信的管道
- SocketChannel 用于TCP网络通信的管道
- DatagramChannel 用于UDP网络通信的管道
这是比较来说常用的几种。
(2)Buffer
缓冲区则是数据在通道传输的载体。
底层是数组结构,可以进行get/set操作 Buffer没有构造器,使用时通过XxxBuffer.allocate(int n)方法进行分配,创建容量为n的对象
Buffer底层示意图
有三个比较重要的属性capacity (容量) limit (界限) position (位置)。
初始化时,position=0,limit=capacity 调用put方法存入数据,positon向后移动,代表数据处理的位置 装载数据完成后,调用flip,切换读写模式,操作索引的方法 读出数据完成后,调用clear,清空初始位置的方法。
用代码片段认识Buffer属性
ByteBuffer buffer = ByteBuffer.allocate(8);
System.out.println("capacity:" + buffer.capacity());
System.out.println("limit:" + buffer.limit());
System.out.println("position:" + buffer.position());
buffer.put("yu".getBytes());
System.out.println("=========存入y&u");
System.out.println("position:" + buffer.position());
buffer.flip();
System.out.println("=========调用flip");
System.out.println("limit:" + buffer.limit());
System.out.println("position:" + buffer.position());
System.out.println("=========读取数据");
// 不传参 代表获取第一个 传参代表指定索引位置
// 当传参有索引时 position不变化
System.out.println( (char) buffer.get());
System.out.println("position:" + buffer.position());
// 标记 存储当前的位置position
buffer.mark();
System.out.println((char) buffer.get());
System.out.println("position:" + buffer.position());
// 回退 退回到mark记录的位置
buffer.reset();
System.out.println("=========调用reset");
System.out.println("position:" + buffer.position());
// clear清空的是索引位置 对象依然存在
buffer.clear();
System.out.println("=========调用clear");
System.out.println("limit:" + buffer.limit());
System.out.println("position:" + buffer.position());
(3)Selector
Selector也有比较重要的三个参数 Selector选择器、SelectableChannel可选择的通道、SelectionKey选择键。
将属性为可选择的通道注册进选择器中,Selector可以监听已注册的通道是否有规定操作产生,使用SelectionKey代表具体的事件(连接、接收、读/写 )。
SelectableChannel,是一个抽象类,提供了通道可被选择需要实现的api。
服务通道、选择器注册
// 服务端通道
private ServerSocketChannel channel;
// 多路复用选择器
private Selector selector;
channel = ServerSocketChannel.open();
selector = Selector.open();
SocketAddress address = new InetSocketAddress(6666);
channel.socket().bind(address);
channel.configureBlocking(false);
// 注册通道的可接收事件
channel.register(selector, SelectionKey.OP_ACCEPT);