天天看點

NIO之坑:完全了解NIO Selector

目錄

    • Selector是什麼
    • 如何建立一個Selector對象
    • 如何将selectable channel注冊到selector中
    • selector如何維護selection keys
    • selector如何選擇就緒channel
    • selector線程安全嗎
    • 原文
    • Selection
    • Concurrency

Selector是什麼

  • Selector是一個或多個SelectableChannel對象的多路複用器.

如何建立一個Selector對象

  • 一個

    selector

    對象可以通過調用Selector.open()來建立,這個工廠方法會使用系統預設的selector provider來建立一個新的selector對象。或者我們還可以通過實作抽象類

    SelectorProvider

    自定義一個selector provider,然後調用它的

    openSelector()

    來建立,

    例如:new SelectorProviderImpl().openSelector()

  • 除非調用selector.close(),否則該

    selector

    将會一直保持

    打開狀态

如何将selectable channel注冊到selector中

  • 通過

    channel

    的register方法,将

    channel

    注冊到給定的

    selector

    中,并傳回一個表示注冊關系的SelectionKey 對象。

selector如何維護selection keys

一個

selector

維護着三個selection keys集合:

  • key set 包含着所有

    selectionKey

    s,目前所有注冊到

    selector

    中的

    channel

    傳回的注冊關系SelectionKey都包含在内,這個集合可以通過selector.keys() 方法傳回。
  • selected-key set 包含着一部分

    selectionKey

    s,其中的每個

    selectionKey

    所關聯的

    channel

    selection operation

    期間被檢測出至少

    準備好

    了一個可以在

    興趣集

    中比對到的操作。這個集合可以通過調用selector.selectedKeys()方法傳回。selected-key set 一定是 key set 的子集。
  • cancelled-key set 也包含着一部分

    selectionKey

    s,其中的每個

    selectionKey

    都已經被

    取消

    ,但是所關聯

    channel

    還沒有被

    撤銷登記

    。cancelled-key set 不能夠被直接傳回,但也一定是 key set 的子集。

對于一個新建立的

selector

其中這三個集合都是空着的。

通過

channel

的register方法,一個

selectionKey

被增加到

selector

的 key set 中。

無論通過channel.close()還是通過selectionKey.cancel()來

取消

一個

selectionKey

,這個

selectionKey

都會被立即添加到

selector

的 cancelled-key set 中,但是所關聯的

channel

并沒有立即被

撤銷登記

,直到發生下次

selection operations

, 這些

channel

才被從

selector

撤銷登記

,與此同時這些Cancelled keys才會被從這個

selector

的所有

selectionKey set

(可能是_key set_、selected-key set、cancelled-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 被移除,同時也可以從它的 key 和 cancelled-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 set 和 selected-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

期間會順序的同步

selector

and

all three key sets

一個

selector

的 key set 和 selected-key set 通常情況下是線程不安全的。如果一個線程想要修改這個集合,需要同步控制它。通過

key集合

的iterator()方法傳回的

Iterators

提供了

快速失敗

(fail-fast):如果在建立疊代器之後修改了set,除了通過調用疊代器自己的remove() 方法之外,将抛出ConcurrentModificationException 。

原文

  • A multiplexor of SelectableChannel objects.

    A selector may be created by invoking the

    open

    method of this class, which will use the system’s default

    selector provider

    to create a new selector. A selector may also be created by invoking the

    openSelector

    method of a custom selector provider. A selector remains open until it is closed via its

    close

    method.

    A selectable channel’s registration with a selector is represented by a

    SelectionKey

    object. A selector maintains three sets of selection keys:
    • The key set contains the keys representing the current channel registrations of this selector. This set is returned by the

      keys

      method.
    • The selected-key set 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

      selectedKeys

      method. The selected-key set is always a subset of the key set.
    • The cancelled-key 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.

    All three sets are empty in a newly-created selector.

    A key is added to a selector’s key set as a side effect of registering a channel via the channel’s

    register

    method. Cancelled keys are removed from the key set during selection operations. The key set itself is not directly modifiable.

    A key is added to its selector’s cancelled-key set when it is cancelled, whether by closing its channel or by invoking its

    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.

    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

    remove

    method or by invoking the

    remove

    method of an

    iterator

    obtained from the 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.

    Selection

    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

    select()

    ,

    select(long)

    , and

    selectNow()

    methods, and involves three steps:
    1. 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.
    2. 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:
      1. 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.
      2. 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.

    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.

    3. If any keys were added to the cancelled-key set while step (2) was in progress then they are processed as in step (1).

    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.

    Concurrency

    Selectors are themselves safe for use by multiple concurrent threads; their key sets, however, are not.

    The selection operations synchronize on the selector itself, on the key set, and on the selected-key set, in that order. They also synchronize on the cancelled-key set during steps (1) and (3) above.

    Changes made to the interest sets of a selector’s keys while a selection operation is in progress have no effect upon that operation; they will be seen by the next selection operation.

    Keys may be cancelled and channels may be closed at any time. Hence the presence of a key in one or more of a selector’s key sets does not imply that the key is valid or that its channel is open. Application code should be careful to synchronize and check these conditions as necessary if there is any possibility that another thread will cancel a key or close a channel.

    A thread blocked in one of the

    select()

    or

    select(long)

    methods may be interrupted by some other thread in one of three ways:
    • 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.
    The

    close

    method synchronizes on the selector and all three key sets in the same order as in a selection operation.

    A selector’s key and selected-key sets are not, in general, safe for use by multiple concurrent threads. If such a thread might modify one of these sets directly then access should be controlled by synchronizing on the set itself. The iterators returned by these sets’

    iterator

    methods are fail-fast: If the set is modified after the iterator is created, in any way except by invoking the iterator’s own

    remove

    method, then a

    ConcurrentModificationException

    will be thrown.