Java NIO vs. IO

在我們學(xué)習(xí) NIOIO 的 API 時(shí)细疚,腦海中會(huì)冒出這個(gè)問(wèn)題:
“什么時(shí)候用 NIO?什么時(shí)候用 IO暑认?”
接下來(lái)我們將對(duì)二者之間的異同點(diǎn)進(jìn)行詳述达皿。

Java NIO 與 IO 之間的主要差異

下面的表格列出了二者間的主要差異,下文將對(duì)這些差異進(jìn)行詳述芥牌。

IO NIO
面向 Stream 面向 Buffer
阻塞 IO 選擇器 Selectors

面向 Stream vs. 面向 Buffer

面向 stream 和 面向 buffer 的不同预吆,意味著什么呢?

Java IO 是面向流的胳泉,意味著你從流中一次讀取一個(gè)或多個(gè)字節(jié),你用讀到的字節(jié)做什么完全取決于你岩遗,字節(jié)沒(méi)有任何緩存扇商。此外,你無(wú)法在流中前后移動(dòng)宿礁。如果你需要在你從流中讀到的數(shù)據(jù)里前后移動(dòng)案铺,那你必須先將它們緩存在緩沖區(qū)中。

Java NIO 面向緩沖區(qū)的方法略有不同梆靖。數(shù)據(jù)被讀到之后處理的緩沖區(qū)中控汉。你可以在緩沖區(qū)中按你的需求前后移動(dòng)。這會(huì)讓你在處理期間有更大的靈活性返吻。然而姑子,為了完整的處理數(shù)據(jù),你需要檢查緩沖區(qū)是否包含了你需要的全部數(shù)據(jù)测僵。還有街佑,你需要確保往緩沖區(qū)寫(xiě)入更多數(shù)據(jù)時(shí),是否會(huì)覆蓋緩沖區(qū)中待處理的數(shù)據(jù)捍靠。

阻塞(Blocking)vs. 非阻塞(Non-blocking) IO

Java IO 的各種流都是阻塞式的沐旨。也就是說(shuō),當(dāng)一個(gè)線程調(diào)用 read()write() 方法榨婆,那個(gè)線程將被阻塞磁携,直到有數(shù)據(jù)讀到或數(shù)據(jù)完全寫(xiě)入為止。該線程在此期間將什么都不做良风。

Java NIO 的非阻塞模式使一個(gè)線程能夠請(qǐng)求從通道中讀數(shù)據(jù)谊迄,僅得到當(dāng)前可讀的數(shù)據(jù)闷供,或者如果當(dāng)前沒(méi)有數(shù)據(jù)可讀時(shí),就什么都得不到鳞上。而不是一直阻塞到數(shù)據(jù)成為可供讀取的狀態(tài)这吻,在此期間線程可以繼續(xù)做其他事情。
該方式同樣適用于非阻塞式寫(xiě)入篙议。一個(gè)線程可以請(qǐng)求向通道中寫(xiě)入一些數(shù)據(jù)唾糯,但是不必等到它完全寫(xiě)入。在此期間線程可以繼續(xù)做其他事情鬼贱。

該線程在非阻塞 IO 調(diào)用期間移怯,會(huì)利用空閑時(shí)間處理其他通道的 IO 請(qǐng)求。也就是說(shuō)这难,單個(gè)線程現(xiàn)在可以管理多個(gè)通道的輸入和輸出舟误。

選擇器(Selectors)

Java NIO 的 選擇器(selectors )可以用單個(gè)線程來(lái)監(jiān)聽(tīng)多個(gè)通道(channels )的輸入狀態(tài)。你可以在一個(gè)選擇器上注冊(cè)多個(gè)通道姻乓,然后用單個(gè)線程去選中(select)那些有輸入信息的通道或準(zhǔn)備進(jìn)行輸出操作的通道來(lái)處理嵌溢。這個(gè)選擇器機(jī)制使得單線程管理多通道的問(wèn)題變得很簡(jiǎn)單。

NIO 和 IO 對(duì)應(yīng)用程序設(shè)計(jì)的影響

無(wú)論你選擇 NIOIO 作為你的 IO 工具蹋岩,都可能從以下幾個(gè)方面影響應(yīng)用程序的設(shè)計(jì):

  1. NIOIO 類(lèi)的API 調(diào)用方式赖草;
  2. 數(shù)據(jù)處理過(guò)程;
  3. 用于處理數(shù)據(jù)的線程數(shù)剪个。

API調(diào)用

采用 IO 來(lái)調(diào)用API秧骑,僅需要從輸入流(例如 InputStream)中讀取字節(jié)數(shù)據(jù),而 NIO 方式則需要先將數(shù)據(jù)讀取到一個(gè)緩沖區(qū)(Buffer)中扣囊,然后在緩沖區(qū)中進(jìn)行處理乎折。
由此來(lái)看,NIOIO 在 API調(diào)用方面差距較大侵歇。

數(shù)據(jù)處理

當(dāng)使用一個(gè)純 NIOIO 方式時(shí)骂澄,對(duì)數(shù)據(jù)處理過(guò)程也有一定的影響。

IO 方式惕虑,你可以從 InputStreamReader 中讀取數(shù)據(jù)字節(jié)酗洒。假設(shè)你正在處理一個(gè)基于行的文本數(shù)據(jù)流,文本如下:

Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

這個(gè)文本行的流可以這樣處理:

InputStream input = ... ; // get the InputStream from the client socket
BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

程序的執(zhí)行狀態(tài)是由程序執(zhí)行了多少來(lái)決定的枷遂。換句話說(shuō)樱衷,一旦第一行 reader.readLine() 方法返回,你肯定知道已經(jīng)讀到了一整行的文本酒唉。因?yàn)?em>readLine() 會(huì)一直阻塞到整行文本讀取完成矩桂。你也知道這行文本中包含了name 信息。同理,當(dāng)?shù)诙?readLine() 調(diào)用返回時(shí)侄榴,你知道這行文本中包含了 age 信息雹锣。

正如你所看到的,只有當(dāng)有新數(shù)據(jù)讀取時(shí)癞蚕,程序才會(huì)進(jìn)行蕊爵,并且每一步你都都知道數(shù)據(jù)是什么。一旦執(zhí)行中的線程已經(jīng)執(zhí)行了讀取某個(gè)數(shù)據(jù)片段的代碼桦山,這個(gè)線程將無(wú)法(幾乎不能)回退數(shù)據(jù)攒射。這個(gè)原理在下圖中有所說(shuō)明:

**Java IO: 從阻塞流中讀取數(shù)據(jù)**

NIO 的實(shí)現(xiàn)看起來(lái)會(huì)有所不同,這是一個(gè)簡(jiǎn)單的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);

注意第二行恒水,從通道將字節(jié)讀入到 ByteBuffer 中会放。當(dāng)方法調(diào)用返回時(shí),你不知道你所需的所有數(shù)據(jù)是否已在緩沖區(qū)中钉凌。這使得處理過(guò)程稍微更難一些咧最。

想象一下,在第一次 read(buffer) 調(diào)用之后御雕,讀入緩沖區(qū)的所有內(nèi)容都是半行矢沿。例如,"Name: An"酸纲。你能處理這些數(shù)據(jù)嗎捣鲸?顯然是不能的。你需要等到至少有一整行數(shù)據(jù)進(jìn)入到了緩沖區(qū)福青,在此之前處理任何數(shù)據(jù)都無(wú)意義。

那么你怎么知道緩沖區(qū)是否包含足夠的數(shù)據(jù)來(lái)使它有被處理的意義呢脓诡?的確无午,你不會(huì)知道。查看緩沖區(qū)中的數(shù)據(jù)是知道的唯一方法祝谚。結(jié)果是宪迟,你可能必須多次檢查緩沖區(qū)中的數(shù)據(jù),然后才知道是否所有數(shù)據(jù)都在內(nèi)部交惯。這種方式效率很低次泽,而且在程序設(shè)計(jì)方面可能變得很亂。例如:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {
    bytesRead = inChannel.read(buffer);
}

bufferFull() 方法必須跟蹤讀取到緩沖區(qū)中的數(shù)據(jù)量席爽,并返回 true 或 false意荤,具體取決于緩沖區(qū)是否已滿。換句話說(shuō)只锻,如果緩沖區(qū)準(zhǔn)備進(jìn)行處理玖像,則認(rèn)為緩沖區(qū)已滿。

bufferFull() 方法掃描緩沖區(qū)齐饮,但必須使緩沖區(qū)處于與調(diào)用 bufferFull() 方法之前相同的狀態(tài)捐寥。否則笤昨,讀入緩沖區(qū)的下一個(gè)數(shù)據(jù)可能不會(huì)在正確的位置讀取。這并非不可能握恳,但它也是另一個(gè)值得注意的問(wèn)題瞒窒。

如果緩沖區(qū)已滿,則可以進(jìn)行處理乡洼。如果它不是滿的崇裁,你也許能夠部分處理這些數(shù)據(jù)(假設(shè)這在你的特定情況下是有意義的,事實(shí)上在大部分情況下是沒(méi)有意義的)就珠。

在下圖中說(shuō)明了緩沖區(qū)內(nèi)數(shù)據(jù)準(zhǔn)備循環(huán)的過(guò)程:

**Java NIO: 從通道讀取數(shù)據(jù)寇壳,直到所有需要的數(shù)據(jù)都在緩沖區(qū)中**

總結(jié)

使用 NIO ,你可以用一個(gè)(或少量)線程來(lái)管理多個(gè)通道(網(wǎng)絡(luò)連接或文件)妻怎。成本是壳炎,解析數(shù)據(jù)可能比在從阻塞流讀取數(shù)據(jù)時(shí)要復(fù)雜得多。

如果你需要管理成千上萬(wàn)個(gè)同時(shí)打開(kāi)的連接逼侦,而且每個(gè)連接上只發(fā)送少量數(shù)據(jù)(比如聊天服務(wù)器)匿辩,那么采用 NIO 方式來(lái)實(shí)現(xiàn)服務(wù)器會(huì)更好。
同樣榛丢,如果你需要與其他電腦保持大量打開(kāi)的連接(例如 P2P 網(wǎng)絡(luò))铲球,使用單個(gè)線程管理所有出站(outbound)連接可能是一個(gè)優(yōu)勢(shì)。下面這個(gè)圖描述了單個(gè)線程管理多連接的設(shè)計(jì):

**Java NIO: 單個(gè)線程管理多個(gè)連接**

如果你需要少量連接數(shù)且非常高的帶寬晰赞,每次發(fā)送大量數(shù)據(jù)稼病,那么采用典型的 IO 服務(wù)器實(shí)現(xiàn)更符合需求。這個(gè)圖展示了典型的 IO 服務(wù)器設(shè)計(jì):

**Java IO: 典型 IO 服務(wù)器設(shè)計(jì)—— 一個(gè)線程處理一個(gè)連接**

原文地址:http://tutorials.jenkov.com/java-nio/nio-vs-io.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末掖鱼,一起剝皮案震驚了整個(gè)濱河市然走,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌戏挡,老刑警劉巖芍瑞,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異褐墅,居然都是意外死亡拆檬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)妥凳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)竟贯,“玉大人,你說(shuō)我怎么就攤上這事逝钥〕嗡#” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)齐莲。 經(jīng)常有香客問(wèn)我痢站,道長(zhǎng),這世上最難降的妖魔是什么选酗? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任阵难,我火速辦了婚禮,結(jié)果婚禮上芒填,老公的妹妹穿的比我還像新娘呜叫。我一直安慰自己,他們只是感情好殿衰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布朱庆。 她就那樣靜靜地躺著,像睡著了一般闷祥。 火紅的嫁衣襯著肌膚如雪娱颊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,879評(píng)論 1 290
  • 那天凯砍,我揣著相機(jī)與錄音箱硕,去河邊找鬼。 笑死悟衩,一個(gè)胖子當(dāng)著我的面吹牛剧罩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播座泳,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惠昔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挑势?” 一聲冷哼從身側(cè)響起镇防,我...
    開(kāi)封第一講書(shū)人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薛耻,沒(méi)想到半個(gè)月后营罢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體赏陵,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饼齿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝙搔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缕溉。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吃型,靈堂內(nèi)的尸體忽然破棺而出证鸥,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布枉层,位于F島的核電站泉褐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鸟蜡。R本人自食惡果不足惜膜赃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望揉忘。 院中可真熱鬧跳座,春花似錦、人聲如沸泣矛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)您朽。三九已至狂丝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虚倒,已是汗流浹背美侦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魂奥,地道東北人菠剩。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像耻煤,于是被迫代替她去往敵國(guó)和親具壮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容