title | date | categories | tags | description | |||
---|---|---|---|---|---|---|---|
Selector |
2020-01-20 |
|
|
|
Selecto能够检测多个注册在它上面的通道上,是否有事件发生。因此,可以先将多个通道(channel)注册到一个Selector上面,注册的时候需要指出当前这个通道(channel)感兴趣的事件是什么。然后 Selector 会一直检测,哪个通道上有事件发生。
当有事件发生的时候,就可以获取事件,然后进行相应的处理。
只有当连接(通道、channel)上面真正有事件(比如读写)发生的时候,才会进行操作(比如读写),这样就不需要阻塞线程,就等通道上的各种事件就绪的时候,再去处理就好了。
比如Read事件,之前使用BIO的时候,read()方法会阻塞在那里,一直等着能读到数据的时候才会返回。
现在用NIO,只要让Selector在那检测就好了, 当它检测到读事件就绪,也就是在通道上已经有数据准备好让我们读了,才会通知我们去获取数据,这样就不需要阻塞了。
/**
* A multiplexor of {@link SelectableChannel} objects.
* SelectableChannel 对象的多路复用器
*
* A selector may be created by invoking the {@link #open open} method of
* this class, which will use the system's default {@link
* java.nio.channels.spi.SelectorProvider selector provider} to
* create a new selector.
* 可以通过 Selector 类的静态方法open()来创建一个 Selector对象, open()方法将会使用系统默认的 SelectorProvider来创建一个新的 selector。
*
* A selector may also be created by invoking the
* {@link java.nio.channels.spi.SelectorProvider#openSelector openSelector}
* method of a custom selector provider.
* 也可以通过 调用自定义的SelectorProvider的openSelector() 方法创建一个selector
*
* A selector remains open until it is
* closed via its {@link #close close} method.
* 一个selector 创建后,将会一直保持打开的状态,直到调用close()方法将它关闭
*
*
* A selectable channel's registration with a selector is represented by a
* {@link SelectionKey} object. A selector maintains three sets of selection
* keys:
* 当一个 channel(连接、通道)注册到一个 selector 上之后, 就用一个 SelectionKey 来表示。就是用selectionKey 代表这个注册关系。
* 一个selector 维护了三个 SelectionKey的集合。
*
*
* The <i>key set</i> contains the keys representing the current
* channel registrations of this selector. This set is returned by the
* {@link #keys() keys} method.
* 第一个集合 key set , 可以理解为全集。里面维护了当前注册到这个selector上的所有的channel的 SelectionKey 对象。用方法 keys() 来返回。
*
* The <i>selected-key set</i> is the set of keys such that each
* key's channel was detected to be ready for at least one of the operations
* identified in the key's interest set during a prior selection operation.
* This set is returned by the {@link #selectedKeys() selectedKeys} method.
* The selected-key set is always a subset of the key set.
* 第二个集合 selected-key set。它代表什么呢?
* (直白的翻译,很难懂)一个 key 对应的 channel 在一段时间内被检测到 存在于 感兴趣的事件列表中的至少一个操作 就绪了, 那么就把这个channel 对应的selectionKey 放到这个集合中。
* 通俗的解释:当一个 channel 注册到selector 的时候,要指定一下自己关注哪些事件,比如 READ 事件。那么,当selector 在一段时间监测的过程中,检测到 channel 上有事件就绪了了,而且这个事件必须是在之前注册时指定的那个感兴趣的列表中的,这时,selector就会把这个 channel 对应的selectionKey 放到这个集合里面。
* 一句话解释:这个集合代表了当前有哪些channel上有事件发生。
* 这个集合是由 selectedKeys()方法返回。 它总是 key set 的子集。
*
* The <i>cancelled-key</i> set is the set of keys that have been
* cancelled but whose channels have not yet been deregistered. This set is
* not directly accessible. The cancelled-key set is always a subset of the
* key set.
* 第三个集合 cancelled-key set , 里面存放的都是那些 被取消了,但是 channel 还没有被注销的 key。
* 这个集合不能直接访问。 这个 cancelled-key set 总是 key set 的 子集。
*
*
* <p> All three sets are empty in a newly-created selector.
* 当selector新创建的时候, 三个集合都是空的。
* 那一个key 是何时被添加到各个集合当中的呢?
*
* <p> A key is added to a selector's key set as a side effect of registering a
* channel via the channel's {@link SelectableChannel#register(Selector,int)
* register} method. Cancelled keys are removed from the key set during
* selection operations. The key set itself is not directly modifiable.
* 当channel 调用 register 方法注册到selector的时候,这个channel 对应的key 就会被添加到key set (说的是上面第一个集合)当中了。
* 我们看下 register 方法的源码就可以看到:
* 先用 findKey() 方法从 key set 中查找,如果找到, 就设置一下感兴趣的事件,然后就返回这个 SelectionKey 对象
* 如果没找到,就表示是新注册的channel,调用底层的 register方法, 并返回一个 SelectionKey, 然后把这个SelectionKey 对象添加到key set 中。
*
* public final SelectionKey register(Selector sel, int ops,
Object att)
throws ClosedChannelException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (blocking)
throw new IllegalBlockingModeException();
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
k = ((AbstractSelector)sel).register(this, ops, att);
addKey(k);
}
}
return k;
}
}
*
* <p> A key is added to its selector's cancelled-key set when it is cancelled,
* whether by closing its channel or by invoking its {@link SelectionKey#cancel
* cancel} method. Cancelling a key will cause its channel to be deregistered
* during the next selection operation, at which time the key will removed from
* all of the selector's key sets.
* 无论是关闭SelectionKey 对应的那个channel, 还是调用 SelectionKey 的 cancel方法,都会将 SelectionKey 添加到 cancelled key set 中
* 取消一个 key 将导致它对应的channel在下一次选择操作期间被注销,这时该key将从selector的所有集合中删除。
*
* Keys are added to the selected-key set by selection
* operations. A key may be removed directly from the selected-key set by
* invoking the set's {@link java.util.Set#remove(java.lang.Object) remove}
* method or by invoking the {@link java.util.Iterator#remove() remove} method
* of an {@link java.util.Iterator iterator} obtained from the
* set.
* Key 在 selection操作的时候, 会被添加到selected-key set 中。说的是啥呢?
* 就是当selector 监听的一系列 channel 上有 事件发生的时候,发生了事件的那个channel 对应的 selectionKey 就会被添加到 selected-key set 中了。
* 准确的说,发生的事件一定要在注册的列表内啊。(发生的事件,必须是这个channel关注的事件才行)
* 可以通过调用 集合 的 remove 方法直接将 key 从 selected-key set 中删除;
* 也可以通过 set 获取到 Iterator对象, 然后调用 Iterator 对象的 remove 方法,将key从 selected-key set 中删除;
*
* Keys are never removed from the selected-key set in any other way;
* they are not, in particular, removed as a side effect of selection
* operations. Keys may not be added directly to the selected-key set.
*
* 决不能以任何其他方式将键从选定键集中删除。 作为选择操作的副作用,尤其不要将其删除。键可能不会直接添加到所选键集中。
*
* <a name="selop"></a>
* <h2>Selection</h2>
*
* During each selection operation, keys may be added to and removed from a
* selector's selected-key set and may be removed from its key and
* cancelled-key sets. Selection is performed by the {@link #select()}, {@link
* #select(long)}, and {@link #selectNow()} methods, and involves three steps:
* 在selection 操作期间,key 可能 从 key set、 cancelled-key set selected-key set 中移除,也可能添加到这几个集合中。
* 执行 selection 操作, 是通过调用 Selector.select()方法, selection操作包括三个步骤:
*
* <ol>
*
* <li><p> Each key in the cancelled-key set is removed from each key set of
* which it is a member, and its channel is deregistered. This step leaves
* the cancelled-key set empty. </p></li>
* 第一步: 遍历 cancelled-key 中的每个 key , 只要 这个key 是 cancelled-key set 、key set、 selected-key set 的成员,都会被移除。
* 并且,这个key对应的channel 将被注销。 这一步会导致 cancelled-key set 为空。
*
* <li><p> The underlying operating system is queried for an update as to the
* readiness of each remaining channel to perform any of the operations
* identified by its key's interest set as of the moment that the selection
* operation began. For a channel that is ready for at least one such
* operation, one of the following two actions is performed: </p>
* 第二步:询问底层操作系统,去查询一下,所有的channel上是否有操作就绪。这个操作必须是在selection操作开始时(注册的时候)指定的那个感兴趣的事件(操作)。
* 对于一个channel而言,如果有至少一个操作就绪,就执行下面两个操作中的一个:
* <ol>
*
* <li><p> If the channel's key is not already in the selected-key set then
* it is added to that set and its ready-operation set is modified to
* identify exactly those operations for which the channel is now reported
* to be ready. Any readiness information previously recorded in the ready
* set is discarded. </p></li>
* 如果 这个 channel 的key 不在 selected-key set,那么就把这个key 加入到 selected-key set,并且 修改 ready-operation set ,来标识该* channel的这些操作已经准备就绪。
* 先前记录在ready-operation set集中的所有准备信息都将被丢弃
*
* <li><p> Otherwise the channel's key is already in the selected-key set,
* so its ready-operation set is modified to identify any new operations
* for which the channel is reported to be ready. Any readiness
* information previously recorded in the ready set is preserved; in other
* words, the ready set returned by the underlying system is
* bitwise-disjoined into the key's current ready set. </p></li>
* 如果 这个 channel 的key 已经存在于 selected-key set, 就修改 ready-operation set 以标识通道已准备就绪的任何新操作。
* 先前记录在ready-operation set中的任何准备就绪信息都将保留。
*
* </ol>
*
* If all of the keys in the key set at the start of this step have empty
* interest sets then neither the selected-key set nor any of the keys'
* ready-operation sets will be updated.
*
* <li><p> If any keys were added to the cancelled-key set while step (2) was
* in progress then they are processed as in step (1). </p></li>
*
* </ol>
*
* <p> Whether or not a selection operation blocks to wait for one or more
* channels to become ready, and if so for how long, is the only essential
* difference between the three selection methods. </p>
* Selector.select() Selector.select(long) Selector.selectNow() 这三个方法的唯一区别是:selection 操作是否阻塞等待一个或者多个channel上有操作准备就绪,以及阻塞多长时间。
*
*
* <h2>Concurrency</h2> 并发,不看了,太难了
*/
-
open()
创建选择器对象。其返回类型,在MacOS 上是: KQueueSelectorImpl -
select()
监听所有注册的通道,当某个通道上有事件发生的时候,则将对应的SelectionKey加入到内部集合并且返回。 -
selectedKeys()
表示在 Selector 监听的时候, 有哪些通道上有感兴趣的事件发生了 -
Selector.keys()
表示所有注册到Selector上的通道对应的key -
select()
和select(long timeout)
方法是阻塞的。直到监听的通道上有事件发生。selectNow()
是非阻塞,立即返回
-
服务端:一个线程上有一个Selector。调用
open()
创建Selector对象。 -
把多个Channel 都注册到Selector 上(注册的时候, 需要告诉Selector自己关注什么类型的事件)。
-
注册后, 返回SelectionKey。 SelectionKey 会被 Selector 管理起来, 放到集合里
protected HashSet<SelectionKey> keys
中。就是我们上面说的全集:key set -
Selector调用
select()
方法, 进行监听。 -
当
select()
方法返回, 表示有事件发生了, 返回int, 就是代表有几个通道发生事件了。 -
然后就可以通过 Selector 的
selectedKeys
集合(就是源码中所说的第二个集合),拿到有哪些通道上面有事件发生了。 -
再通过selectionKey可以获取到Channel
-
通过channel完成业务读写业务