Java NIO 入門

JimHeimbach 7年前發布 | 11K 次閱讀 NIO Java Java開發

很多人用過 InputStream 和 OutputStream 接口,用來操作 文件 、 Socket 等等 IO 操作。

如果是簡單的,速度較快的 IO 操作,我們用 Stream 類的接口,依然可以風生水起。

如果你要使用非阻塞的 IO 的話,他們可能就滿足不了你了。

熟悉操作系統的人會知道,操作非阻塞 IO 無非幾種多路復用:

  1. select
  2. poll
  3. epoll
  4. kqueue
  5. IOCP

這里的復用模型有幾個是操作系統相關的——也就是說,并不是所有的操作系統都可以用,典型的就是 IOCP 是 Windows 的”專利”, kqueue 是 BSD 的”專利”(比如macOS)。

那么 java 作為一門跨平臺的語言解決方案,是如何在虛擬機上使用 non-blocking IO 的呢?

具體的實現我們可以不管,它使用了 Selector 的 API,調用方式非常類似 select 。

Channel & ByteBuffer vs Stream

在 nio 中,不再使用 Stream API 對 Socket 進行交互,而是使用 Channel 和 ByteBuffer 進行交互,

Channel 負責管道的工作, ByteBuffer 負責緩存的工作。

原先 InputStream 和 OutputStream 的工作就由 Channel 做掉了,如果這個 Channel 支持 Select 模型的話,它就是 SelectableChannel 的子類。

那么,在消息循環的模型中,首先要建立循環,像我們的 Looper.loop() 一樣,我們先用 Selector.open() 新建一個 Selector

Selector eventSelector = Selector.open();

// 設置這個 channel 是非阻塞的
socketChannel.configureBlocking(false);

// 注冊到 selector 里,并設置好關心的事件
socketSelectionKey = socketChannel.register(eventSelector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

Selector

接下來調用 eventSelector.select() 阻塞,就能在你關心的事件到來的時候,阻塞就會被喚醒,處理事件。

sample:

while (connected) {
    eventSelector.select();
    Set<SelectionKey> keys = eventSelector.selectedKeys();
    Iterator<SelectionKey> iterator = keys.iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isReadable()) {
            // 當 socket 可讀
            internalOnRead((ReadableByteChannel) key.channel());
        }

        if (key.isWritable()) {
            // 當 socket 可寫
            internalOnWrite((WritableByteChannel) key.channel());
        }
        iterator.remove();
    }
}

總結

nio 對于客戶端的優勢幾乎沒有,但是可以讓代碼更好管理; 如果這時候你使用的是 ServerSocket ,好處就立馬體現了,因為你的業務需求很可能是這樣:

  1. master 線程,開啟 accept.
  2. 如果有客戶接入,開啟一個 worker,用來服務 client。
  3. 服務完后,保持或者關閉這個連接。

(這個業務模型類似Apache httpd)這樣的業務模型可能導致過多的線程開銷,使得并發量并不高。

那么,老生常談的 event-driven 的模型在 java 中,就差不多是這樣的邏輯:

  1. master 線程,開啟 selector, 并為 ServerSocket 注冊 accept, read, write 等事件。
  2. 客戶接入,為 client socket 注冊 read, write 事件,依舊在該線程里面進行循環。
  3. 當 event trigger 的時候,處理相關業務邏輯。

第二個模型只啟動了一個線程,所有的 IO 操作都在 OS 里面完成了,用戶空間內的資源消耗大大降低,這也是我們把 Server 端的 IO 改成 nio 的優勢。

 

來自:https://geminiwen.xyz/2017/02/21/learn-nio/

 

 本文由用戶 JimHeimbach 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!