模块 java.base

类 Selector

java.lang.Object
java.nio.channels.Selector
所有已实现的接口:
Closeable , AutoCloseable
已知子类:
AbstractSelector

public abstract class Selector extends Object implements Closeable
SelectableChannel 对象的多路复用器。

可以通过调用此类的open 方法来创建选择器,该方法将使用系统默认的selector provider 创建一个新的选择器。也可以通过调用自定义选择器提供程序的 openSelector 方法来创建选择器。选择器保持打开状态,直到通过其 close 方法将其关闭。

可选通道与选择器的注册由 SelectionKey 对象表示。选择器维护三组选择键:

  • 键组包含表示此选择器的当前通道注册的键。该集合由 keys 方法返回。

  • 选择键集是一组键,使得每个键的通道被检测到准备好在先前的选择操作期间在键的兴趣集中标识的至少一个操作,该选择操作添加键或更新集合中的键。该集合由 selectedKeys 方法返回。选定键集始终是键集的子集。

  • 取消键set 是已取消但其通道尚未注销的密钥集。该集合不可直接访问。取消键集始终是键集的子集。

在新创建的选择器中,所有三个集合都是空的。

作为通过通道的 register 方法注册通道的副作用,将一个键添加到选择器的键集中。在选择操作期间,取消的键从键集中移除。密钥集本身不可直接修改。

当一个键被取消时,无论是通过关闭它的通道还是通过调用它的 cancel 方法,它都会被添加到它的选择器的取消键集中。取消一个键将导致其通道在下一次选择操作期间被注销,此时该键将从所有选择器的键集中删除。

通过选择操作将键添加到选定键集中。通过调用集合的 remove 方法或调用从集合中获得的 iterator remove 方法,可以直接从所选键集中删除一个键。通过调用集合的 clear 方法,可以从选定键集中删除所有键。不能将键直接添加到选定键集中。

选择

选择操作向底层操作系统查询有关每个已注册通道是否准备好执行由其键的兴趣集标识的任何操作的更新。选择操作有两种形式:

  1. select() select(long) selectNow() 方法将准备执行操作的通道的键添加到选定键集中,或更新已在选定键集中的准备好操作的键集。

  2. select(Consumer) select(Consumer, long) selectNow(Consumer) 方法执行一个行动在准备执行操作的每个通道的键上。这些方法不会添加到所选键集中。

添加到选定键集的选择操作

在每个选择操作期间,可以将键添加到选择器的选定键集中或从中删除,并且可以从其键和取消键集中删除。选择由 select() select(long) selectNow() 方法执行,包括三个步骤:

  1. cancelled-key set 中的每个 key 都从它所属的每个 key set 中删除,并且它的通道被注销。此步骤将 cancelled-key 集留空。

  2. 在选择操作开始的那一刻,查询底层操作系统以获取关于每个剩余通道是否准备好执行由其键的兴趣集标识的任何操作的更新。对于准备好进行至少一个此类操作的通道,将执行以下两个操作之一:

    1. 如果通道的键不在所选键集中,则将其添加到该组中,并修改其就绪操作集以准确标识通道现在报告为就绪的那些操作。任何先前记录在就绪集中的就绪信息都将被丢弃。

    2. 否则,通道的键已经在选定键集中,因此它的就绪操作集被修改以识别报告通道准备就绪的任何新操作。任何以前记录在就绪集中的就绪信息都会被保留;换句话说,底层系统返回的就绪集按位分解为键的当前就绪集。

    如果此步骤开始时键集中的所有键都具有空的兴趣集,则所选键集和任何键的准备操作集都不会更新。
  3. 如果在步骤 (2) 进行时有任何密钥被添加到已取消密钥集中,则它们将按照步骤 (1) 中的方式进行处理。

选择操作是否阻止等待一个或多个通道准备就绪,如果是,等待多长时间,是三种选择方法之间唯一的本质区别。

对选定键执行操作的选择操作

在每个选择操作期间,可以从选择器的键、选定键和取消键集中删除键。选择由 select(Consumer) select(Consumer,long) selectNow(Consumer) 方法执行,包括三个步骤:

  1. cancelled-key set 中的每个 key 都从它所属的每个 key set 中删除,并且它的通道被注销。此步骤将 cancelled-key 集留空。

  2. 在选择操作开始的那一刻,查询底层操作系统以获取关于每个剩余通道是否准备好执行由其键的兴趣集标识的任何操作的更新。

    对于至少准备好一个这样的操作的通道,通道键的就绪操作集被设置为准确标识通道准备好进行的操作和行动调用 select 方法以使用通道的密钥。之前记录在就绪集中的任何就绪信息在调用行动.

    或者,当通道准备好进行多个操作时,行动可以多次调用,通道的键和就绪操作集被修改为通道就绪的操作的子集。在哪里行动为同一个键调用多次,那么它的就绪操作集永远不会包含之前调用时包含在集合中的操作位行动在同一个选择操作中。

  3. 如果在步骤 (2) 进行时有任何密钥被添加到已取消密钥集中,则它们将按照步骤 (1) 中的方式进行处理。

并发

Selector 及其键集可供多个并发线程安全使用。但是,它的选定键集和取消键集不是。

选择操作在选择器本身、选定键集上按此顺序同步。它们还在上面的步骤 (1) 和 (3) 期间在已取消的密钥集上进行同步。

在进行选择操作时,对选择器键的兴趣集所做的更改对该操作没有影响;下一次选择操作将看到它们。

密钥可能会被取消,通道可能会随时关闭。因此,在一个或多个选择器的键集中存在一个键并不意味着该键是有效的或者它的通道是打开的。如果另一个线程有可能取消一个键或关闭一个通道,应用程序代码应该小心地同步并在必要时检查这些条件。

在选择操作中阻塞的线程可能会以三种方式之一被其他线程中断:

  • 通过调用选择器的 wakeup 方法,

  • 通过调用选择器的 close 方法,或者

  • 通过调用阻塞线程的 interrupt 方法,在这种情况下,将设置其中断状态并调用选择器的 wakeup 方法。

close 方法以与选择操作中相同的顺序在选择器及其选定键集上同步。

Selector 的键集可以安全地供多个并发线程使用。从密钥集中检索操作通常不会阻塞,因此可能与添加到集合中的新注册重叠,或者与从集合中删除密钥的选择操作的取消步骤重叠。迭代器和拆分器返回反映集合在创建迭代器/拆分器时或创建后的某个时间点的状态的元素。他们不会抛出 ConcurrentModificationException

通常,选择器的选定键集对于多个并发线程的使用是不安全的。如果这样的线程可能会直接修改集合,那么应该通过同步集合本身来控制访问。集合的 iterator 方法返回的迭代器是快速失败:如果在创建迭代器之后修改集合,除了调用迭代器自己的 remove 方法之外,以任何方式修改,然后将抛出 ConcurrentModificationException

自从:
1.4
参见:
  • 构造方法总结

    构造方法
    修饰符
    构造方法
    描述
    protected
    初始化此类的新实例。
  • 方法总结

    修饰符和类型
    方法
    描述
    abstract void
    关闭此选择器。
    abstract boolean
    告知此选择器是否打开。
    abstract Set<SelectionKey>
    keys()
    返回此选择器的键集。
    static Selector
    open()
    打开一个选择器。
    返回创建此通道的提供者。
    abstract int
    选择一组键,其对应的通道已准备好进行 I/O 操作。
    abstract int
    select(long timeout)
    选择一组键,其对应的通道已准备好进行 I/O 操作。
    int
    选择相应通道已准备好进行 I/O 操作的键并对其执行操作。
    int
    select(Consumer<SelectionKey> action, long timeout)
    选择相应通道已准备好进行 I/O 操作的键并对其执行操作。
    abstract Set<SelectionKey>
    返回此选择器的选定键集。
    abstract int
    选择一组键,其对应的通道已准备好进行 I/O 操作。
    int
    选择相应通道已准备好进行 I/O 操作的键并对其执行操作。
    abstract Selector
    使第一个还没有返回的选择操作立即返回。

    在类 java.lang.Object 中声明的方法

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • 构造方法详细信息

    • Selector

      protected Selector()
      初始化此类的新实例。
  • 方法详情

    • open

      public static Selector  open() throws IOException
      打开一个选择器。

      新选择器是通过调用系统范围默认 SelectorProvider 对象的 openSelector 方法创建的。

      返回:
      一个新的选择器
      抛出:
      IOException - 如果发生 I/O 错误
    • isOpen

      public abstract boolean isOpen()
      告知此选择器是否打开。
      返回:
      true 当且仅当此选择器打开时
    • provider

      public abstract SelectorProvider  provider()
      返回创建此通道的提供者。
      返回:
      创建此通道的提供商
    • keys

      public abstract Set <SelectionKey > keys()
      返回此选择器的键集。

      密钥集不可直接修改。一个密钥只有在它被取消并且它的通道被注销后才会被删除。任何修改键集的尝试都会导致抛出 UnsupportedOperationException

      该集合是 safe 供多个并发线程使用。

      返回:
      此选择器的键集
      抛出:
      ClosedSelectorException - 如果这个选择器关闭
    • selectedKeys

      public abstract Set <SelectionKey > selectedKeys()
      返回此选择器的选定键集。

      键可以从选定键集中删除,但不能直接添加到选定键集中。任何向键集添加对象的尝试都会导致抛出 UnsupportedOperationException

      选择的键集是 不是线程安全的

      返回:
      此选择器的选定键集
      抛出:
      ClosedSelectorException - 如果这个选择器关闭
    • selectNow

      public abstract int selectNow() throws IOException
      选择一组键,其对应的通道已准备好进行 I/O 操作。

      此方法执行非阻塞 选择操作 。如果自上次选择操作以来没有通道变为可选,则此方法立即返回零。

      调用此方法会清除任何先前调用 wakeup 方法的影响。

      返回:
      键的数量,可能为零,其就绪操作集现在指示至少一类操作的准备就绪,而这些操作之前未检测到通道已准备就绪
      抛出:
      IOException - 如果发生 I/O 错误
      ClosedSelectorException - 如果这个选择器关闭
    • select

      public abstract int select(long timeout) throws IOException
      选择一组键,其对应的通道已准备好进行 I/O 操作。

      此方法执行阻塞 选择操作 。它仅在至少选择一个通道、调用此选择器的wakeup 方法、当前线程被中断或给定的超时期限到期后返回,以先到者为准。

      此方法不提供实时保证:它像调用 Object.wait(long) 方法一样安排超时。

      参数:
      timeout - 如果为正,则阻塞最多 timeout 毫秒,或多或少,同时等待通道准备就绪;如果为零,则无限期阻塞;不得为负
      返回:
      键的数量,可能为零,其就绪操作集现在指示至少一类操作的准备就绪,而这些操作之前未检测到通道已准备就绪
      抛出:
      IOException - 如果发生 I/O 错误
      ClosedSelectorException - 如果这个选择器关闭
      IllegalArgumentException - 如果超时参数的值为负
    • select

      public abstract int select() throws IOException
      选择一组键,其对应的通道已准备好进行 I/O 操作。

      此方法执行阻塞 选择操作 。它仅在至少选择一个通道、调用此选择器的wakeup 方法或当前线程被中断后返回,以先到者为准。

      返回:
      键的数量,可能为零,其就绪操作集现在指示至少一类操作的准备就绪,而这些操作之前未检测到通道已准备就绪
      抛出:
      IOException - 如果发生 I/O 错误
      ClosedSelectorException - 如果这个选择器关闭
    • select

      public int select(Consumer <SelectionKey > action, long timeout) throws IOException
      选择相应通道已准备好进行 I/O 操作的键并对其执行操作。

      此方法执行阻塞 选择操作 。仅当至少选择了一个通道、调用此选择器的 wakeup 方法、当前线程被中断或给定超时期限到期时(以先到者为准),它才会从查询操作系统中唤醒。

      指定的行动accept 方法是用每个通道的键调用的,每个通道都准备好执行由其键的兴趣集标识的操作。 accept 方法可以针对同一个键调用多次,但准备操作集包含通道准备就绪的操作的子集(如上所述)。 accept 方法在选择器及其选定键集上同步时被调用。必须非常小心,避免与也在这些对象上同步的其他线程发生死锁。选择操作一般是不可重入的,因此行动应该非常小心,不要尝试对同一个选择器进行选择操作。尝试重入选择操作时的行为是特定于实现的,因此未指定。如果行动关闭选择器,然后在操作完成时抛出 ClosedSelectorException。这行动不禁止关闭向选择器注册的通道,也不禁止取消密钥或更改密钥的兴趣集。如果选择了一个通道,但在选择之前取消了它的密钥或者改变了它的兴趣设置行动是在密钥上执行的,那么它是否是特定于实现的行动被调用(它可以用 invalid 键调用)。操作抛出的异常被转发给调用者。

      此方法不提供实时保证:它像调用 Object.wait(long) 方法一样安排超时。

      实现要求:
      默认实现从选定键集中删除所有键,在给定超时时调用select(long) ,然后对添加到选定键集中的每个键执行操作。默认实现不检测执行可重入选择操作的操作。在完成默认实现时,所选键集可能为空,也可能不为空。
      参数:
      action - 要执行的操作
      timeout - 如果为正,则阻塞最多 timeout 毫秒,或多或少,同时等待通道准备就绪;如果为零,则无限期阻塞;不得为负
      返回:
      消耗的唯一键的数量,可能为零
      抛出:
      IOException - 如果发生 I/O 错误
      ClosedSelectorException - 如果此选择器已关闭或被操作关闭
      IllegalArgumentException - 如果超时参数的值为负
      自从:
      11
    • select

      public int select(Consumer <SelectionKey > action) throws IOException
      选择相应通道已准备好进行 I/O 操作的键并对其执行操作。

      此方法执行阻塞 选择操作 。仅当至少选择了一个通道、调用此选择器的wakeup 方法或当前线程被中断时(以先到者为准),它才会从查询操作系统中唤醒。

      这个方法相当于调用 2-arg select 超时时间为 0 的方法无限阻塞。

      实现要求:
      默认实现调用超时为 0 的 2-arg select 方法。
      参数:
      action - 要执行的操作
      返回:
      消耗的唯一键的数量,可能为零
      抛出:
      IOException - 如果发生 I/O 错误
      ClosedSelectorException - 如果此选择器已关闭或被操作关闭
      自从:
      11
    • selectNow

      public int selectNow(Consumer <SelectionKey > action) throws IOException
      选择相应通道已准备好进行 I/O 操作的键并对其执行操作。

      此方法执行非阻塞 选择操作

      调用此方法会清除任何先前调用 wakeup 方法的影响。

      实现要求:
      默认实现从选定键集中删除所有键,调用 selectNow() 然后对添加到选定键集中的每个键执行操作。默认实现不检测执行可重入选择操作的操作。在完成默认实现时,所选键集可能为空,也可能不为空。
      参数:
      action - 要执行的操作
      返回:
      消耗的唯一键的数量,可能为零
      抛出:
      IOException - 如果发生 I/O 错误
      ClosedSelectorException - 如果此选择器已关闭或被操作关闭
      自从:
      11
    • wakeup

      public abstract Selector  wakeup()
      使第一个还没有返回的选择操作立即返回。

      如果另一个线程当前在选择操作中被阻塞,那么该调用将立即返回。如果当前没有正在进行的选择操作,则下一次调用选择操作将立即返回,除非同时调用了 selectNow() selectNow(Consumer) 。在任何情况下,该调用返回的值都可能是非零的。随后的选择操作将像往常一样阻塞,除非同时再次调用此方法。

      在两个连续的选择操作之间多次调用此方法与只调用一次具有相同的效果。

      返回:
      这个选择器
    • close

      public abstract void close() throws IOException
      关闭此选择器。

      如果线程当前在此选择器的选择方法之一中被阻塞,则它会被中断,就像调用选择器的 wakeup 方法一样。

      任何仍与该选择器关联的未取消的键都将失效,它们的通道将被注销,并且与该选择器关联的任何其他资源将被释放。

      如果此选择器已关闭,则调用此方法无效。

      选择器关闭后,任何进一步尝试使用它,除非调用此方法或 wakeup 方法,否则将导致抛出 ClosedSelectorException

      指定者:
      close 在接口 AutoCloseable
      指定者:
      close 在接口 Closeable
      抛出:
      IOException - 如果发生 I/O 错误