2018-10-24
NIO基础入门(六)之选择器selectors
1.概念
使单线程能够有效的管理多个I/O通道,非阻塞的方式节省资源消耗
主要由三个有关类组成
2.组成
- Selector选择器
- SelectableChannel 可选择通道
- SelectionKey 选择键
注意:选择器才是管理功能的对象
1.Selector选择器
选择器类管理着一个被注册的通道集合的信息和就绪状态
通道和选择器一起被注册,并通过选择器来更新通道状态
方法
open()
建立选择器对象
isOpen()
判断选择器对象是否建立
close()
provider()
select()
可立刻或指定睡眠时间后准备好I/O,进行调用
selectNow()
wakeup()
停止选择的过程(退出select睡眠)
keys()
selectedKeys()
2.SelectableChannel可选择通道
这个抽象类提供了实现通道可选择性所需要的公共方法
所有的socket和Pipe都是可选择的,但是FileChannel不是可选择的(没有继承SelectableChannel父类)
- SelectableChannel可以被注册到选择器,并且可以指定哪个选择器
- 一个通道可以被注册到多个选择器(后续注册的通道策略更新之前的策略),但一个选择器只能注册一次
方法
register()
注册通道到这顶选择器
isRegistered()
通道是否被注册
keyFor()
返回该通道与选择器之间关系的选择键对象(没有注册关系则返回null)
configureBlocking()
isBlocking()
blockingLock()
以上三个为非阻塞相关方法
调用register()方法将可选择通道注册到选择器
只能注册非阻塞通道,否则异常;而且该通道无法改回阻塞通道
试图注册已关闭的通道也会抛出异常
3.SelectionKey选择键
选择键封装了特定的通道与特定的选择器之间的注册关系(和操作策略,读/写/连接/接收等)
validOps()方法可以获得指定通道支持哪些操作策略
选择器对象被SelectableChannel.register()返回并提供一个表示注册关系的标记
标记:该注册关系关心的通道操作和通道已经准备好的操作
方法
(可以提前通过keyFor获得选择键对象)
(可通过通道注册选择器返回该对象)
channel()
返回与该键相关的通道对象
selector()
返回与该键相关的选择器对象
cancel()
终结通道和选择器的注册关系(注册不会立刻消失,但键会立刻失效)
isValid()
判断通道和选择器是否有注册关系
interestOps()
interest策略集合,如果需要这些操作策略,从该方法中获得
选择器下次调用select()时生效
readyOps()
ready策略集合,如果需要这些操作策略,从该方法中获得
当前select()操作即可生效
if((key.readyOps()&SelectionKeyOP_READ)!=0)
isReadable()
是否准备好读
isWritable()
是否准备好写
isConnectable()
是否准备好连接
isAcceptable()
是否准备好接收
attach()
保存一个任意对象作为附件,选择键除了保存他,不会再其他地方调用,可以存放业务对象/会话句柄/其他通道等等
attachment()
通过该方法获得附件,如果没有附件,则返回null
3.选择器操作
1.建立选择器
1 | SocketChannel channel1 = SocketChannel.open(); |
2.关闭选择器
1 | if (selector.isOpen()){ |
3.选择器维护三种键
- 已注册键的集合Registered key set,通过keys()方法返回,可能是空
- 已选择键的集合(注册建集合的子类)Selected key set,这些键包还在interset集合的操作,selectedKeys()返回,可能是空
- 已取消键的集合Cancelled key set,包含了cancel()方法被调用过得键,还没有被注销,但已无效(下次select时注销)
4.停止选择过程
有三种方法停止
- Selector中的wakeup(),使线程从被阻塞(睡眠)的select()方法中优雅的退出;
- Selector中的close(),阻塞线程被唤醒,但是与选择器相关的通道会被注销,键也会被取消
- Selector中的interrupt(),中断,如果继续执行会抛出异常
以上三个方法只会中断选择器,并不会中断通道
5.异步关闭能力
当一个通道被关闭,选择器的select()并不会受影响,这意味着调用select()之前仍然有效,但是返回时无效
6.选择过程的可扩展性(重点)
相对于使用一个线程来管理所有通道而言,在分布式不同业务环境下可能不是最好的选择
一个更好的策略是(负载):
对所有可选择通道使用一个选择器,然后对就绪通道的服务委托给其他线程.
- 这样可以通过一个线程来监控通道的就绪状态
- 通过一个协调好的线程池来处理接受到的数据
另一个场景:
某些通道要求比其他通道更高的响应速度
可以使用两个选择器管理,
- 一个为命令连接服务
- 一个为普通连接服务
这样类似于第一种策略,但分配更为精细,会根据情况使用多个线程池来为准备好的通道提供服务,
如:日志线程池,命令/控制线程池,状态请求线程池等等