-
Notifications
You must be signed in to change notification settings - Fork 360
Java NIO之Selector(选择器)
Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。
通过调用Selector.open()方法创建一个Selector对象,如下:
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
Channel必须是费阻塞的 所以FileChannel不适用Selector,因为FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承SelectableChannel。Socket Channel可以正常使用。
SelectableChannel抽象类有一个**configureBlocking()**方法用于使通道处于阻塞模式或非阻塞模式。
abstract SelectableChannel configureBlocking(boolean block)
SelectableChannel抽象类的configureBlocking()方法是由AbstractSelectableChannel抽象类实现的,SocketChannel、ServerSocketChannel、DatagramChannel都是直接继承了AbstractSelectableChannel抽象类。
register()方法的第二个参数。这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:
- Connect
- Accept
- Read
- Write
通道触发了一个事件意思时该事件已经就绪。比如某个Channel成功链接到另一个服务器称为“连接就绪”。一个Server Socket Channel准备好接收新进入的连接称为“接收就绪”。一个有数据可读的通道可以说是“读就绪”。等待写好数据的通道可以说是“写就绪”。
这四种事件用SelectionKey的四个常理来表示:
* SelectionKey.OP_CONNECT
* SelectionKey.OP_ACCEPT
* SelectionKey.OP_READ
* SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,使用或与运算符即可,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
* key.attachment();//返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
* key.channel();//返回该SelectionKey对应的channel
* key.selector();//返回该SelectionKey对应的Selector
* key.interestOps();//返回代表需要Selector监控的IO操作的bit mask
* key.readyOps();//返回一个bit mask,代表在相应channel上可以进行的IO操作
key.interestOps();
我们可以通过以下方法来判断Selector是否对Channel的某种事件感兴趣
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
key.readyOps();
ready集合是通道已经准备就绪的操作的集合。Java中定义以下几个方法用来检查这些操作是否就绪。
//创建ready集合的方法
int readySet = selectionKey.readOps();
//检查这些操作是否就绪的方法
boolean isReadable();//是否可读
boolean isWritable();//是否可写
boolean isConnectable();//是否可连接
boolean isAcceptable();//是否可接收
从SelectionKey访问Channel和Selection很简单。如下:
Channel channel = key.channel();
Selector selector = key.selector();
key.attachment();
可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个给定的通道。例如,可以附加与通道一起使用的Buffer,或是包含聚集数据的某个对象。使用这个方法如下:
key.attach(theObject);
Object attachedObj = key.attachment();
还可以在用register()方法向Selector注册Channel的时候附加对象。如:
SelectionKey key = channel.register(selector,SelectionKey.OP_READ,theObject);
选择器维护注册过的通道的集合,并且这种注册关系都被封装在SelectionKey当中
Selector维护的三种类型SelectionKey集合:
- 已注册的键的集合(Registered Key set)
所有与选择器关联的通道所生成的键的集合称为已经注册的键的集合。并不是所有注册过的键都仍然有效。这个集合通过Keys()方法返回,并且可能是空的。这个已注册的键的集合不是可以直接修改的;试图这么做的话讲引发UnsupportedOperationException。
- 已选择的键的集合(Selected key set)
所有与选择器关联的通道所生成的键的集合称为已经注册的键的集合。并不是所有注册过的键都仍然有效。这个集合通过keys()方法返回,并且可能是空的。这个已注册的键的集合不是可以直接修改的;试图这么做的话将引发UnsupportedOperationException。
公众号:UncleCatMySelf