第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Java NIO Selector 介紹

1. 前言

前面小節(jié)介紹 Java TCP Socket 編程時,我們遇到了“創(chuàng)建的 TCP server 不能同時接收多個客戶端請求”的問題。我們的解決方案是采用多線程模型,即每線程模型或者是線程池模型。然而多線程模型會創(chuàng)建大量的線程,消耗大量系統(tǒng)資源。后來進(jìn)入了 Java NIO 編程的學(xué)習(xí),我們說編寫高性能服務(wù)器必須采用非阻塞式 Socket 編程。然而,通過 Java NIO 的編程示例可以發(fā)現(xiàn):相比阻塞式 Socket 編程,非阻塞式 Socket 編程的難度大了一個數(shù)量級。我們需要應(yīng)用好 I/O 多路事件處理機(jī)制,需要處理好數(shù)據(jù)收發(fā)的各種情況,而 I/O 事件多路復(fù)用機(jī)制是整個非阻塞 Socket 編程的核心。

其實,I/O 多路復(fù)用機(jī)制(I/O Multiplex)最早是由操作系統(tǒng)提供的,有一套專用的系統(tǒng) API。目前主流操作系統(tǒng)提供的 I/O 多路復(fù)用 API 如下:

  • select,是通用機(jī)制,Windows、Unix-like 系統(tǒng)都支持。
  • poll, 是 UNIX-like 系統(tǒng)支持。
  • devpoll,是 SUN Solaris 系統(tǒng)支持。當(dāng)然,SUN 公司已經(jīng)不存在了。
  • epoll, 是 Linux 系統(tǒng)支持的主流機(jī)制。
  • Kqueue,是 freebsd 內(nèi)核支持的機(jī)制,Mac OS、IOS 系統(tǒng)也支持。
  • IOCP,是 Windows 系統(tǒng)支持的機(jī)制。

對于 Java 來說,也有自己的 I/O 多路復(fù)用機(jī)制,那就是 Java NIO Selector。

2. Java NIO Selector 工作原理

Java NIO 四個核心的組件分別是 Selector、SocketChannel、ServerSocketChannel、SelectionKey。Selector 是 I/O 事件反應(yīng)器,是動力源。SocketChannel、ServerSocketChannel、SelectionKey 都是功能組件,它們之間互相配合,如下:

圖片描述

  • 首先創(chuàng)建一個 Selector 對象,然后調(diào)用它的 select 方法,進(jìn)入事件等待狀態(tài)。
  • 對于服務(wù)器來說,需要創(chuàng)建 ServerSocketChannel 對象,然后調(diào)用它的 register 方法,將 SelectionKey.OP_ACCEPT 事件注冊到 Selector,準(zhǔn)備監(jiān)聽新的客戶端連接。
  • 如果 Selector 監(jiān)聽到新的客戶端連接請求,SelectionKey.OP_ACCEPT 事件就會產(chǎn)生。調(diào)用 ServerSocketChannel 的 accept 方法,返回一個 SocketChannel 對象,需要將 SocketChannel 的 SelectionKey.OP_READ 事件注冊到 Selector。
  • 在上面兩步中, ServerSocketChannel 和 SocketChannel 都提供了 register 方法,返回值是 SelectionKey。SelectionKey 中綁定了上下文信息。
  • 如果 Selector 監(jiān)聽到 I/O 事件,它的 select 方法就會返回??梢哉{(diào)用 Selector 的 selectedKeys 方法,返回一個 SelectionKey 數(shù)組,包含了所有產(chǎn)生了 I/O 事件的 SocketChannel。遍歷這個數(shù)組,逐個處理相應(yīng)的 I/O 事件。

3. Java Selector API 介紹

3.1 創(chuàng)建實例

Selector 只聲明了一個 protected 構(gòu)造方法,構(gòu)造 Selector 實例只能通過它的工廠方法 open,聲明如下:

public static Selector open() throws IOException

3.2 注冊事件

Selector 接口并沒有聲明 register 方法,而是通過它的抽象實現(xiàn)類提供了一個 abstract protected 方法,也沒有對外暴露。聲明如下:

protected abstract SelectionKey register(AbstractSelectableChannel ch,int ops, Object att);

Selector 的 register 機(jī)制最終委派給了 AbstractSelectableChannel 類。為此,我們要想將 Channel 注冊到 Selector,需要調(diào)用 AbstractSelectableChannel 的 register 方法。聲明如下:

public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException

參數(shù)說明:

  • sel 是預(yù)先創(chuàng)建的 Selector 對象。
  • ops 表示需要注冊的具體事件。支持的事件類型如下:
    - SelectionKey.OP_ACCEPT   表示監(jiān)聽客戶端的連接,用于服務(wù)器
    - SelectionKey.OP_CONNECT  表示非阻塞式客戶端連接過程,用于客戶端
    - SelectionKey.OP_READ     表示監(jiān)聽讀事件
    - SelectionKey.OP_WRITE    表示監(jiān)聽寫事件
    
  • att 用于保存上下文對象。

3.3 監(jiān)聽事件

Selector 提供了 select 方法用于監(jiān)聽 I/O 事件,聲明如下:

public abstract int select() throws IOException
public abstract int select(long timeout) throws IOException

當(dāng)沒有 I/O 事件產(chǎn)生時,調(diào)用 select 方法的線程會被阻塞。如果你調(diào)用無參 select 方法,線程進(jìn)入等待狀態(tài),直到有 I/O 事件發(fā)生才返回。如果你調(diào)用包含了 timeout 參數(shù)的 select 方法,線程會在 timeout 超時,或者是有 I/O 事件發(fā)生返回。select 的返回值表示產(chǎn)生了 I/O 事件的 SelectionKey 的個數(shù)。

3.4 遍歷事件

當(dāng)有 I/O 事件發(fā)生,Selector 的 select 方法會返回??梢酝ㄟ^ Selector 的 selectedKeys 方法,獲取所有產(chǎn)生了 I/O 事件的 SelectionKey。聲明如下:

 public abstract Set<SelectionKey> selectedKeys()

方法的返回值是一個 SelectionKey 類型的集合,我們需要遍歷此集合,逐個處理。遍歷的方法如下:

Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if (key != null) {
        if (key.isAcceptable()) {
            // ServerSocketChannel 接收了一個新連接
        } else if (key.isConnectable()) {
            // 表示一個新連接建立
        } else if (key.isReadable()) {
            // Channel 有準(zhǔn)備好的數(shù)據(jù),可以讀取
        } else if (key.isWritable()) {
            // Channel 有空閑的 Buffer,可以寫入數(shù)據(jù)
        }
    }
    keyIterator.remove();
}

3.5 SelectionKey 介紹

SelectionKey 是由 AbstractSelectableChannel 的 register 方法返回的,主要包含一個事件類型和上下文對象。SelectionKey 提供了一組方法,用以識別 I/O 事件類型。聲明如下:

public final boolean isAcceptable()
public final boolean isConnectable()
public final boolean isReadable()
public final boolean isWritable()

可以通過 SelectionKey 的 channel 方法,獲取關(guān)聯(lián)的 Channel,聲明如下:

public abstract SelectableChannel channel()

可以通過 SelectionKey 的 attachment 方法,獲取關(guān)聯(lián)的上下文對象。

public final Object attachment()

SelectionKey 的各個方法相對簡單,容易理解,我們在前面小節(jié)多次提到,不再贅述。

4. 總結(jié)

本小結(jié)主要是介紹 Java NIO Selector 機(jī)制的工作原理。關(guān)于 Selector 機(jī)制,不僅 Java 支持,各大操作系統(tǒng)都有支持,是編寫高性能服務(wù)器的利器。一般在依賴倒轉(zhuǎn)模型中,充當(dāng)動力源、反應(yīng)器的角色。