Selector是多个SelectableChannel对象的多路复用器。//一个Selector对应多个SelectableChannel。
Selector对象可以通过调用Selector.open()方法创建,这将会使用系统默认的SelectorProvider来创建一个新的Selector。Selector对象也可以通过调用(自定义的SelectorProvider对象的)openSelector()方法创建。Selector会一直保持打开状态直到通过调用close()方法关闭它。
一个SelectionKey对象代表:一个SelectableChannel注册到一个Selector上。一个Selector维护着三个SelectionKey集合:
- key set 包含所有SelectionKey的代表了当前所有Channel向Selector的注册。这个集合通过keys()方法获得。
- selected-key set 包含了这样的一些SelectionKey:SelectionKey对应的Channel在之前的select过程中被检测到已经准备好至少一种操作,这种操作被定义在SelectionKey关注的操作集合中。
//Channel向Selector注册时要指定关注的操作集,即SelectableChannel#register方法的第二个参数。
这个集合通过调用selectedKeys()方法获得。这个集合总是key set 的子集。 - cancelled-key set 包含了一些SelectionKey,它们已经被取消了,但是它们对应的Channel还没有取消注册。这个集合不可以直接访问。这个集合总是key set 的子集。
这三个集合在新创建的Selector中都是空的。
当调用SelectableChannel#register方法的时候,一个SelectionKey会被加入到Selector的key set 中。被取消的SelectionKey会在select过程中从key set里移除。key set 本身不可以直接修改。
当一个SelectionKey被取消时,它会被加入到对应Selector的cancelled-key set 中,无论是通过关闭对应的Channel或调用SelectionKey#cancel方法。取消一个SelectionKey会导致它对应的Channel在下一次select过程中被取消注册,此时这个SelectionKey会从Selector的所有集合中移除。
SelectionKey会通过select加入selected-key set 。一个SelectionKey可以通过调用Set#remove方法或Iterator#remove方法(Iterator通过Set#iterator方法获得)直接从selected-key set 中移除。SelectionKey不会通过其他任何方式从selected-key set 中移除,//用完一次要移除。
他们不会通过select自动被移除(作为select的副作用)。SelectionKey不能直接添加到selected-key set 中。
Selection
在每一次select过程中,SelectionKey可能会被添加到selected-key set 或从selected-key set 删除,且可能会从key set 或 cancelled-key set 删除。select通过select(),select(long),selectNow()方法执行,它包括三步:
- 每一个在cancelled-key set 中的SelectionKey都会从三个集合中移除,它对应的Channel会被取消注册。这一步会清空cancelled-key set 。
- 查询底层操作系统,获取剩下的每个Channel在select开始时的准备状态;准备状态是指Channel对应的SelectionKey关注的操作集中,那些操作的准备状态。对于一个准备好进行至少一种操作的Channel,执行以下两个动作之一:
- 如果Channel对应的SelectionKey并非已存在于 selected-key set 中,那么这个SelectionKey会被添加进去,且这个SelectionKey的准备就绪操作集(SelectionKey#readyOps)会被修改为当前Channel确已准备好的操作。准备就绪操作集中任何之前的准备就绪信息记录都会被丢弃。
- 如果Channel对应的SelectionKey已存在于selected-key set 中,那么它的准备就绪操作集中会新增Channel准备好的操作。任何之前的准备就绪信息记录都会被保留;也可以说是操作系统返回的就绪集与当前SelectionKey的就绪集做按位与运算。
- 当步骤(2)进行时,如果有任何SelectionKey被添加到cancelled-key set 中,那么它们按步骤(1)进行处理。
select是否使线程阻塞等待到有Channel准备就绪,等待多长时间,这是三种select方法唯一的区别。
Concurrency
Selector自身可供多个线程安全的并发使用,但是它的SelectionKey集合不可以。
select会在Selector对象上,在key set ,在selected-key set 上按顺序同步。select也会在上面所说的步骤(1)和步骤(3)时的cancelled-key set 上同步,
在一次select过程中,对SelectionKey关注的操作集做改变不会在本次select中生效;这些改变会在下一次select时被观察到。
SelectionKey和Channel可能会在任何时候被取消和关闭。因此,存在于Selector键集中的SelectionKey并不意味着是有效(valid)的,其对应的Channel也不一定是打开(open)的。如果其他线程有可能取消SelectionKey或关闭Channel,应用程序代码应该小心地去同步和检查这些必要的条件。
一个阻塞在select()或select(long)方法上的线程可能会被另一个线程通过以下三种方式打断:
- 调用Selector#wakeup方法
- 调用Selector#close方法
- 调用被阻塞线程的Thread#interrupt方法,在这种情况下,线程的中断状态会被设置,且对应Selector的wakeup方法会被调用。
close方法在Selector对象上,在三个键集上按顺序同步。//与select时一样
一个SelectionKey和selected-key set 通常不能安全的被多个并发线程使用。如果一个线程可能会直接修改键集,那么需要在键集上同步以控制访问。通过键集的Set#iterator方法得到的Iterator对象是fail-fast的:除了调用Iterator#remove方法外,如果集合在迭代器被创建后发生了改变,ConcurrentModificationException会被抛出。