Java nio 之 选择器selector基础


本文整理自《Java NIO》一书。


选择器Selector类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。当这么做的时候,可以选择将被激发的线程挂起,直到有就绪的的通道。当您调用一个选择器对象的select( )方法时,相关的键建会被更新,用来检查所有被注册到该选择器的通道。您可以获取一个键的集合selectedKeys(),从而找到当时已经就绪的通道。通过遍历这些键,您可以选择出每个从上次您调用select( )开始直到现在,已经就绪的通道。


SelectableChannel可以被注册到Selector对象上,同时可以指定对那个选择器而言,那种操作是感兴趣的。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。注意:FileChannel对象不是可选择的,因为它们没有继承SelectableChannel。所有socket通道都是可选择的,包括从管道(Pipe)对象的中获得的通道。


选择键SelectionKey对象被SelectableChannel.register( ) 返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数的形式进行编码),指示了该注册关系所关心的通道操作,以及通道已经准备好的操作。


通道在被注册到一个选择器上之前,必须先设置为非阻塞模式(通过调用configureBlocking(false))。如果您试图注册一个处于阻塞状态的通道,register( )将抛出未检查的IllegalBlockingModeException异常。此外,通道一旦被注册,就不能回到阻塞状态。试图这么做的话,将在调用configureBlocking( )方法时将抛出IllegalBlockingModeException异常。


选择器才是提供管理功能的对象,而不是可选择通道对象。选择器对象对注册到它之上的通道执行就绪选择,并管理选择键。

Selector selector = Selector.open( ); 
channel1.register (selector, SelectionKey.OP_READ);
channel2.register (selector, SelectionKey.OP_WRITE); 
channel3.register (selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); 
// Wait up to 10 seconds for a channel to become ready 
int readyCount = selector.select (10000);
上述代码创建了一个新的选择器,然后将这三个(已经存在的) socket通道注册到选择器上,而且感兴趣interest的操作各不相同。select( )方法在将线程置于睡眠状态,直到这些感兴趣的事情中的操作中的一个发生或者10秒钟的时间过去。在JDK中,有四种被定义的可选择操作:读(read),写(write),连接(connect)和接受(accept)。并非所有的操作都在所有的可选择通道上被支持。例如, SocketChannel不支持accept。试图注册不支持的操作将导致IllegalArgumentException。您可以通过调用validOps( )方法来获取特定的通道所支持的操作集合。


任何一个通道和选择器的注册关系都被封装在一个SelectionKey对象中。SelectableChannel#keyFor(Selector sel )方法将返回与该通道和指定的选择器相关的键。如果通道被注册到指定的选择器上,那么相关的键将被返回。如果它们之间没有注册关系,那么将返回null。


一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。您可以看到前两个方法中反映了这种关系。channel( )方法返回与该键相关的SelectableChannel对象,而selector( )则返回相关的Selector对象。键对象表示了一种特定的注册关系。当应该终结这种关系的时候,可以调用SelectionKey对象的cancel( )方法。可以通过调用isValid( )方法来检查它是否仍然表示一种有效的关系。当键被取消时,它将被放在相关的选择器的已取消的键的集合里。注册不会立即被取消,但键会立即失效。当再次调用select( )方法时(或者一个正在进行的select()调用结束时),已取消的键的集合中的被取消的键将被清理掉,并且相应的注销也将完成。通道会被注销,而新的SelectionKey将被返回。当通道关闭时,所有相关的键会自动取消(记住,一个通道可以被注册到多个选择器上)。当选择器关闭时,所有被注册到该选择器的通道都将被注销,并且相关的键将立即被无效化(取消)。一旦键被无效化,调用它的与选择相关的方法就将抛出CancelledKeyException。


SelectionKey#attach( )方法将在键对象中保存所提供的对象的引用。SelectionKey类除了保存它之外,不会将它用于任何其他用途。任何一个之前保存在键中的附件引用都会被替换。可以使用null值来清除附件。可以通过调用attachment( )方法来获取与键关联的附件句柄。如果没有附件,或者显式地SelectionKey对象包含的ready集合与最近一次选择器对所注册的通道所作的检查相同。而每个单独的通道的就绪状态会同时改变。通过null方法进行过设置,这个方法将返回null。注意:如果选择键的存续时间很长,但您附加的对象不应该存在那么长时间,请记得在完成后清理附件。否则,您附加的对象将不能被垃圾回收,您将会面临内存泄漏问题。SelectableChannel类的一个register( )方法的重载版本接受一个Object类型的参数。这是一个方便您在注册时附加一个对象到新生成的键上的方法。以下代码:

SelectionKey key = channel.register (selector, SelectionKey.OP_READ, myObject);
// <==>
SelectionKey key = channel.register (selector, SelectionKey.OP_READ);
key.attach (myObject);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值