(3)select 操作返回的值是 ready 集合在步骤 2 中被修改的键的数量,而不是已选择的键的集合中的通道的总数。返回值不是已准备好的通道的总数,而是从上一个 select( )调用之后进入就绪状态的通道的数量。
(4)就绪选择selectNow()是完全非阻塞的。如果当前没有通道就绪,它将立即返回0
2、停止选择过程 (1)有三种方式可以唤醒在select( )方法中睡眠的线程: 1)wakeup() 2)close() 3)interrupt()
如果睡眠中的线程的 interrupt( )方法被调用,它的返回状态将被设置。如果被唤醒的线程之后将试图在通道上执行 I/O 操作,通道将立即关闭,然后线程将捕捉到一个异常。 请注意这些方法中的任意一个都不会关闭任何一个相关的通道。中断一个选择器与中断一个通道是不一样的
3、管理选择键 (1) 选择是累积的(selectKeys是积累的)。一旦一个选择器将一个键添加到它的已选择的键的集合中,它就不会移除这个键。清理选择器中已选择集合中已处理操作的键交给了程序员
while(true)
{
selector.select();
Set
keys=selector.selectedKeys();
Iterator it=keys.iterator();
while(it.hasNext()){
SelectionKey key=it.next();
it.remove();
//处理key
}
(2)当通道上的至少一个感兴趣的操作就绪时,键的 ready 集合就会被清空,并且当前已经就绪的操作将会被添加到ready集合中。该键之后将被添加到已选择的键的集合中。
(3)清理一个 SelectedKey 的 ready 集合的方式是将这个键从selector已选择的键的集合(selectedKeys)中移除。选择键的就绪状态只有在选择器对象在选择操作过程中才会修改。
(4)通常的做法是在选择器上调用一次 select 操作(这将更新已选择的键的集合),然后遍历 selectKeys( )方法返回的键的集合。在按顺序进行检查每个键的过程中,相关的通道也根据键的就绪集合进行处理。然后键将从已选择的键的集合中被移除(通过在 Iterator对象上调用 remove( )方法),然后检查下一个键。完成后,通过再次调用 select( )方法重复这个循环。
(3)在多线程的场景中,如果您需要对任何一个键的集合进行更改,不管是直接更改还是其他操作带来的副作用,您都需要首先以相同的顺序,在同一对象上进行同步。锁的过程是非常重要的。如果竞争的线程没有以相同的顺序请求锁,就将会有死锁的潜在隐患。如果您可以确保否其他线程不会同时访问选择器,那么就不必要进行同步了。
(4)Selector 类的 close( )方法与 select( )方法的同步方式是一样的,因此也有一直阻塞的可能性。在选择过程还在进行的过程中,所有对 close( )的调用都会被阻塞,直到选择过程结束,或者执行选择的线程进入睡眠。
(5) 使用 select( )来为多个通道提供服务(例子selector.SelectSockets)
四、异步关闭能力 1、一个特定的键的集合中的一个键的存在并不保证键仍然是有效的,或者它相关的通道仍然是打开的。
2、当一个通道关闭时,它相关的键也就都被取消了。这并不会影响正在进行的select( ),但这意味着在您调用select( )之前仍然是有效的键,在返回时可能会变为无效。
五、选择过程的可扩展性 1、不同通道请求不同的服务类的办法: 1)在第一个场景中,如果您想要将更多的线程来为通道提供服务,一个更好的策略是对所有的可选择通道使用一个选择器,并将对就绪通道的服务委托给其他线程。您只用一个线程监控通道的就绪状态并使用一个协调好的工作线程池来处理共接收到的数据。(阻塞 编程) 这就是以前处理客户端和服务器端的方式了,不过线程的开销太大了,这种基于阻塞的编程并不能很好的利用到 系统的资源
2)第二个场景中,某些通道要求比其他通道更高的响应速度,可以通过使用两个选择器来解决:一个为命令连接服务,另一个为普通连接服务。但这种场景也可以使用与第一个场景十分相似的办法来解决。与将所有准备好的通道放到同一个线程池的做法不同,通道可以根据功能由不同的工作线程来处理。它们可能可以是日志线程池,命令/控制线程池,状态请求线程池 这种处理的方法就是Netty基于NIO + reactor的处理模式了