NIO之坑:完全理解NIO Selector

1,640 阅读5分钟

Selector是什么


如何创建一个Selector对象


如何将selectable channel注册到selector中

   SelectionKey key = channel.register(selector,Selectionkey.XXX); 
  • 通过channelregister方法,将channel注册到给定的selector中,并返回一个表示注册关系的SelectionKey 对象。

selector如何维护selection keys

  • 一个selector维护着三个selection keys集合:

    • key set 包含着所有selectionKeys,当前所有注册到selector中的channel返回的注册关系SelectionKey都包含在内,这个集合可以通过selector.keys() 方法返回。

    • selected-key set 包含着一部分selectionKeys,其中的每个selectionKey所关联的channelselection operation期间被检测出至少 准备好 了一个可以在兴趣集中匹配到的操作。这个集合可以通过调用selector.selectedKeys()方法返回。selected-key set 一定是 key set 的子集。

    • cancelled-key set 也包含着一部分selectionKeys,其中的每个selectionKey都已经被取消,但是所关联channel还没有被撤销登记cancelled-key set 不能够被直接返回,但也一定是 key set 的子集。

    对于一个新创建的selector其中这三个集合都是空着的。

    通过channelregister方法,一个selectionKey被增加到selectorkey set 中。

    无论通过channel.close()还是通过selectionKey.cancel()取消一个selectionKey ,这个selectionKey都会被立即添加到selectorcancelled-key set 中,但是所关联的channel并没有立即被撤销登记,直到发生下次 selection operations, 这些channel才被从selector撤销登记,与此同时这些Cancelled keys才会被从这个selector的所有selectionKey set(可能是_key set_、selected-key setcancelled-key set)中移除,但是不会影响这些集合本身。

    selection operations 期间,一些selectionKey会被选中添加到 selected-key set 中。其中的每个key可以通过selectiedKeys.remove()selectiedKeys.iterator().remove()直接从 selected-key set 中移除,除此之外不能够通过任何方式被直接移除。特殊的,selected-key set 中的keys还可以在 selection operations 期间被间接移除。但是是不可以直接向 selected-key set 添加key的。


selector如何选择就绪channel

  • 每次 selection operation 期间, keys都可以从selector's selected-key set 被添加或者移除,同时也可以从它的 keycancelled-key sets 被移除。 selection operation 可以被触发通过执行selector.select()selector.select(long),和selector.selectNow() 方法,并且这些方法涉及到以下三个步骤:
  1. 首先每个位于 cancelled-key set 中的key会从每个包含它的key集合中被移除,并且对应的channel会被撤销登记。这个步骤使得 cancelled-key set 变为空。

  2. 查询底层操作系统来获得关于selector中剩余channel就续事件selection operation 开始截止到此刻的更新情况,只要哪个channel就续事件的更新部分有至少一个与兴趣集中的操作匹配上,那么将会执行以下两个动作:

    1. 如果这个channel's key 没有存在selected-key set 那么将它添加到这个集合中,并将它的就绪操作集(ready-operation set)修改成 只包含使得channel被报告就绪的操作,任何先前记录在就绪操作集中的就绪信息都会被丢弃。

    2. 否则,如果这个channel's key 存在selected-key set ,那么就保留就绪操作集中先前的就绪信息,并将这些 使得channel被报告就绪的操作 写入进去;总而言之,系统底层会通过按位与&操作更新当前就绪集。

    如果这些Key兴趣集为空,那么 selected-key set 和 keys'的就续集(ready-operation sets)都不会被更新。

  3. 如果在步骤(2)正在进行时将任何key添加到 cancelled-key set,则按步骤(1)处理它们。

  • selection operations 是否会阻塞等待一个或多个通道准备就绪,以及等待多长时间,这是三种选择方法之间唯一的本质区别。

selector线程安全吗

多线程并发情况下Selectors本身是线程安全的,但是他们所持有的key sets不是线程安全的。

selection operations 按顺序在selector本身,key setselected-key set 上同步。 它们还在上面的步骤(1)和(3)期间在 canceled-key set 上同步。

selection operations 期间改变key兴趣集,对于本次操作将不会产生任何影响;它们的影响将会再下次 selection operations 期间发生。

selectionKey可能会被取消,channel可能随时关闭。 因此,在一个或多个选择器的key集中存在并不意味着selectionKey有效或其channel是开放的。有可能另一个线程取消selectionKey或关闭一个channel,应用程序代码应该小心地同步并检查这些条件。

一个线程通过selector.select()selector.select(long)方法产生的阻塞可以被其他线程用以下三种方式的任意一种来中断:

  • By invoking the selector's wakeup() method,

  • By invoking the selector's close() method, or

  • By invoking the blocked thread's interrupt() method, in which case its interrupt status will be set and the selector's wakeup() method will be invoked.

selector.close()selection operations 期间会顺序的同步selectorand all three key sets

一个selectorkey setselected-key set 通常情况下是线程不安全的。如果一个线程想要修改这个集合,需要同步控制它。通过key集合iterator()方法返回的Iterators提供了快速失败fail-fast):如果在创建迭代器之后修改了set,除了通过调用迭代器自己的remove() 方法之外,将抛出ConcurrentModificationException