Java NIO――3 非阻塞与selector(一)

2014-11-24 07:37:02 · 作者: · 浏览: 2
好吧,先看一下通道选择的三个关键的类Selector、SelectionKey、SocketChannel、channel的关系 \
一、选择器基础 1、选择器提供选择执行已经就绪的任务的能力,这使得多元 I/O 成为可能。

2、您需要将之前创建的一个或多个可选择的通道注册到选择器对象中。一个表示通道和选择器的键将会被返回。选择键会记住您关心的通道。它们也会追踪对应的通道是否已经就绪。当您调用一个选择器对象的 select( )方法时,相关的键集会被更新,用来检查所有被注册到该选择器的通道。您可以获取一个键的集合,从而找到当时已经就绪的通道。通过遍历这些键,您可以选择出每个从上次您调用select( )开始直到现在,已经就绪的通道。 selector.select(); //这方法会阻塞

3、传统监控多个 socket 的 Java 解决方案是为每个 socket 创建一个线程并使得线程可以在 read( )调用中阻塞,直到数据可用。这事实上将每个被阻塞的线程当作了socket监控器,并将 Java 虚拟机的线程调度当作了通知机制。这两者本来都不是为了这种目的而设计的。程序员和 Java 虚拟机都为管理所有这些线程的复杂性和性能损耗付出了代价,这在线程数量的增长失控时表现得更为突出。
而真正的就绪选择必须由操作系统来做。操作系统的一项最重要的功能就是处理 I/O 请求并通知各个线程它们的数据已经准备好了。选择器类提供了这种抽象,使得 Java 代码能够以可移植的方式,请求底层的操作系统提供就绪选择服务。

4、选择器,可选择通道和选择键类 (1)选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。
(2)一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。
(3)选择键selectionkey封装了特定的通道与特定的选择器的注册关系。 由选择键对象被SelectableChannel.register( ) 返回
(4)通道在被注册到一个选择器上之前,必须先设置为非阻塞模式(通过调用configureBlocking(false))。通道一旦被注册,就不能回到阻塞状态。 通道创建默认都是非阻塞编 socketchannel.configureBolcking(false); socketchannel.register(selector, [注册类型]);
(5)一个给定的通道可以被注册到多于一个的选择器上,而且不需要知道它被注册了哪个 Selector 对象上。
(6)选择器才是提供管理功能的对象,而不是可选择通道对象。选择器对象对注册到它之上的通道执行就绪选择,并管理选择键。

5、建立选择器 (1)需要调用close( )方法来释放它可能占用的资源并将所有相关的选择键设置为无效。
(2)选择器包含了注册到它们之上的通道的集合。在任意给定的时间里,对于一个给定的选择器和一个给定的通道而言,只有一种注册关系是有效的。
(3)一个例外的情形是当您试图将一个通道注册到一个相关的键已经被取消的选择器上,而通道仍然处于被注册的状态的时候。通道不会在键被取消的时候立即注销。直到下一次操作发生为止,它们仍然会处于被注册的状态。在这种情况下,未检查的 CancelledKeyException将会被抛出。请务必在键可能被取消的情况下检查SelectionKey 对象的状态。
(4)isRegistered()方法在一个键被取消之后,直到通道被注销为止,可能有时间上的延迟。这个方法只是一个提示,而不是确切的答案。


二、使用选择键 (1)当键被取消时,它将被放在相关的选择器的已取消的键的集合里。注册不会立即被取消,但键会立即失效。当再次调用 select( )方法时(或者一个正在进行的 select()调用结束时),已取消的键的集合中的被取消的键将被清理掉,并且相应的注销也将完成。
(2)当通道关闭时,所有相关的键会自动取消(记住,一个通道可以被注册到多个选择器上)。当选择器关闭时,所有被注册到该选择器的通道都将被注销,并且相关的键将立即被无效化(取消)。
(3)当相关的 Selector 上的 select( )操作正在进行时改变键的 interest 集合(interestOps : int),不会影响那个正在进行的选择操作。所有更改将会在 select( )的下一个调用中体现出来。
(4)ready 集合(readyOps : int)是 interest集合的子集,并且表示了 interest 集合中从上次调用 select( )以来已经就绪的那些操作。
(5)通过相关的选择键的readyOps( )方法返回的就绪状态指示只是一个提示,不是保证。
(6)总体上说,SelectionKey 对象是线程安全的,但知道修改 interest 集合的操作是通过 Selector 对象进行同步的是很重要的。这可能会导致 interestOps( )方法的调用会阻塞不确定长的一段时间。选择器所使用的锁策略(例如是否在整个选择过程中保持这些锁)是依赖于具体实现的。幸好,这种多元处理能力被特别地设计为可以使用单线程来管理多个通道。被多个线程使用的选择器也只会在系统特别复杂时产生问题。坦白地说,如果您在多线程中共享选择器时遇到了同步的问题,也许您需要重新思考一下您的设计。

// 重新注册感兴趣的OP_READ事件 key.interestOps(key.interestOps() " SelectionKey.OP_READ); //取消对OP_READ事件的注册 key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));


三、使用选择器 1、选择过程 (1)选择操作是当三种形式的 select( )中的任意一种被调用时,由选择器执行的。不管是哪一种形式的调用,下面步骤将被执行:
\
select 在selector中的处理过程,之后 \

1)已取消的键的集合(canceledKeys)将会被检查。如果它是非空的,每个已取消的键的集合中的键将从另外两个集合中移除,并且相关的通道将被注销。这个步骤结束后,已取消的键的集合将是空的。
2)已注册的键的集合中的键的 interest 集合(intertestOps)将被检查。在这个步骤中的检查执行过后,对interest集合的改动不会影响剩余的检查过程。
(2)一旦就绪条件被定下来,底层操作系统将会进行查询,以确定每个通道所关心的操作的真实就绪状态。依赖于特定的 select( )方法调用,如果没有通道已经准备好,线程可能会在这时阻塞,通常会有一个超时值。
(3)直到系统调用完成为止,这个过程可能会使得调用线程睡眠一段时间,然后当前每个通道的就绪状态将确定下来。对于那些还没准备好的通道将不会执行任何的操作。对于那些操作系统指示至少已经准备好interest集合中的一种操作的通道,将执行以下两种操作中的一种:
a.如果通道的键还没有处于 selectedKeys 集合中,那么键的 ready 集合(readyOps)将被清空,然后表示操作系统发现的当前通道已经准备好的操作的比特掩码将被设置。
b.否则,也就是键在 selectedKeys 集合中。键的 ready 集合( readyOps )将被表示操作系统发现的当前已经准备好的操作的比特掩码更新。所有之前的