Java的NIO,什麼是blocking? 什麼是non-blocking?

Java NIO

Java語言原本的輸出入(Input/Output)是以java.io這個套件裏的類別來達成接收與傳送資料的目的,但問題是,使用read()這類讀取資料方法時,是以等待對方傳送資料到達時才進行後續的程式處理,如此一來常造成系統運行的瓶頸,因為在資料還未到達時系統還是會留有資源在那等待。JDK自從1.4版開始有了新的處理方式-NIO(Non-blocking I/O),NIO以「非等待式」或稱「非堵塞式」(non-blocking)的方式進行資料的接收,它提供一種「觀察並通知」的機制,讓程式設計的方法有所改變,當需要讀取資料時,不用讓程式痴痴地等待資料的到來,而是讓資料到達時自動通知的模式。
網路的傳輸通常會花費一些時間在等待遠端主機傳送來的資料上,不像在本機存取磁碟檔案的有效率。在原java.io的處理機制裏,雖然可以使用執行緒機制,為這些耗費時間的工作指派指派一個獨立的執行緒專門來處理資料的接收,但產生一個執行緒代表系統必需分配一塊資源供其使用,當程式必需處理上百個甚至上千個連線時,系統資源會大量的耗費。而且,當我們必須在各個執行緒中傳遞資料時,耗費的CPU與記憶體資源也就明顯的增加了。總體來看,耗費資源較少的NIO機制,適合用於設計大量連線的伺服器應用,目的是盡可能降低伺服器的負荷,如果是簡單資料傳輸需求,則可採用java.io即可。
NIO在字面上常會被解釋成New IO,而它並不是要「取代」原java.io的功能,其目的是補充Java語言在處理輸出入的方法。因此,它提供了Channel與Buffer兩種工具類別,Channel是與資料來源建立的通道,Buffer則是用來存放由通道讀取而來的緩衝區。因為java.io讀取/輸出資料時是以位元組為單位,而NIO是以區塊(Block)為單位,讀取與輸出的效能更高。
NIO的類別置於java.nio套件目錄下,而且因為類別功能不同,在這套件下有幾個子套件,說明如下:

  1. java.nio.*

此套件內有NIO有關緩衝區類別,如Buffer、ByteBuffer、CharBuffer等。

  1. java.nio.channels.*

定義了各類通道(Channels),通道是與I/O設備的連接要件,例如檔案與網路Socket,此套件亦定義了選擇器(Selector),選擇器的功能是觀察與監視,程式設計者將所需要的通道告知擇擇器,當有通道中有事件發生時(如有客戶端連線或資料傳輸),選擇器會通知並傳回一組SelectionKey,開發人員可由SelectionKey得到該通道並設計必要的程式碼。

  1. java.nio.channels.spi.*

java.nio.channels套件中類別設計的「服務提供者」類別。

  1. java.nio.charset.*

定義在轉換Unicode字元與位元組資料之間的傳換類別,或稱為解碼器與編碼器類別。

  1. java.nio.charset.spi.*

類別CharsetProvider是java.nio.charset套件類的服務提供者類別。

NIO常用類別

  • InetSocketAddress

java.net.InetAddress不是NIO的類別,它代表了一個主機的資訊,內容包含IP位址與主機名稱,而InetSocketAddress則加上了埠號資訊,代表一台主機的位址與埠號資料。常用的建構子有:

public InetSocketAddress(InetAddress addr, int port)

由傳入值addr位址物件再加上port埠號值而生成物件。

public InetSocketAddress(String host, int port)

傳入主機名稱或IP位址的字串與port埠號,例如產生ptt.cc與port 23:

InetSocketAddress addr = new InetSocketAddress("ptt.cc", 23);
  • Channel

NIO使用Channel來產生與遠端主機或檔案的通道,java.nio.channels.Channel介面定義了各類通道的基礎方法,例如讀取資料的read方法與傳送資料的write方法等,而有許多各類功用的通道類別皆實作了Channel介面,如可用來傳送位元組的WritableByteChannel類別,與讀取位元組的ReadableByteChannel類別,以及用來與遠端主機建立通道的SocketChannel類別等,該套件有三個類別常應用於網路程式設計:
   1. SocketChannel

適用於TCP協定的資料讀取與傳送。

  2. SocketServerChannel

用於TCP協定的伺服器,傾聽本機的埠號,等待外來的連線。

  3. DatagramChannel

用於UDP協定的封包傳送。

通常使用TCP協定的SocketChannel類別,並呼叫SocketChannel.open(主機位址)方法得到與主機的連線通道物件:

InetSocketAddress addr = new InetSocketAddress("ptt.cc", 23);
SocketChannel chann = SocketChannel.open(addr);

open方法將建立連線通道,若失敗則會拋出輸出入例外,因此需預先處理IOException。
NIO對資料的讀取與傳送都需要使用到另一個要角「緩衝區Buffer」,讀取資料時使用read方法,由通道的另一端將資料讀取至緩衝區內。傳送資料時先將資料放在緩衝區中,再呼叫Channel的write方法,以送出資料:
SocketChannel類的的read與write方法規格如下:

  • public int read(ByteBuffer buf)

讀取遠端資料並放置於緩衝區(buf)中。

  • public int write(ByteBuffer)

先將欲傳送出的資料放在緩衝區中,再傳送至遠端主機。

初步認識Java NIO的類別與其用途後,接下來將對Buffer緩衝區進行較深入的介紹與實例。

版權聲明
本文章授權範圍僅限綠豆湯網站使用,除Facebook之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。

Comments

No comments yet. Why don’t you start the discussion?

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *