Java NIO與IO

在研究Java NIO和IO API時,很快就會出現(xiàn)一個問題:

我什么時候應(yīng)該使用IO,什么時候應(yīng)該使用NIO?

在本文中晨缴,我將嘗試闡明Java NIO和IO之間的差異,它們的用例以及它們?nèi)绾斡绊懘a的設(shè)計峡捡。

Java NIO和IO的主要區(qū)別

下表總結(jié)了Java NIO和IO之間的主要區(qū)別击碗。我將在表格后面的部分中詳細(xì)介紹每個區(qū)別。

IO NIO
流導(dǎo)向 緩沖導(dǎo)向
阻止IO 非阻塞IO
選擇器

面向流的與面向緩沖的

Java NIO和IO之間的第一個重要區(qū)別是IO是面向流的们拙,其中NIO是面向緩沖區(qū)的稍途。那么,這意味著什么砚婆?

面向流的Java IO意味著你可以從流中一次讀取一個或多個字節(jié)械拍。你對讀取的字節(jié)做什么取決于你。它們不會緩存在任何地方装盯。此外坷虑,你無法在流中的數(shù)據(jù)中前后移動。如果需要在從流中讀取的數(shù)據(jù)中前后移動埂奈,則需要先將其緩存在緩沖區(qū)中迄损。

Java NIO的面向緩沖區(qū)的方法略有不同。數(shù)據(jù)被讀入緩沖區(qū)账磺,稍后處理該緩沖區(qū)芹敌。你可以根據(jù)需要在緩沖區(qū)中前后移動。這使你在處理過程中具有更大的靈活性垮抗。但是氏捞,你還需要檢查緩沖區(qū)是否包含完整處理所需的所有數(shù)據(jù)。并且冒版,你需要確保在將更多數(shù)據(jù)讀入緩沖區(qū)時液茎,不要覆蓋尚未處理的緩沖區(qū)中的數(shù)據(jù)。

阻止與非阻塞IO

Java IO的各種流都是阻塞的。這意味著豁护,當(dāng)線程調(diào)用read()或write()時哼凯,該線程將被阻塞,直到有一些數(shù)據(jù)要讀取楚里,或者數(shù)據(jù)被完全寫入。在此期間猎贴,該線程無法執(zhí)行任何其他操作班缎。

Java NIO的非阻塞模式允許線程請求從通道讀取數(shù)據(jù),并且只獲取當(dāng)前可用的內(nèi)容她渴,或者根本沒有數(shù)據(jù)达址,如果當(dāng)前沒有數(shù)據(jù)可用。線程可以繼續(xù)使用其他內(nèi)容趁耗,而不是在數(shù)據(jù)可供讀取之前保持阻塞狀態(tài)沉唠。

非阻塞寫作也是如此。線程可以請求將某些數(shù)據(jù)寫入通道苛败,但不要等待它完全寫入满葛。然后線程可以繼續(xù)并在同一時間做其他事情。

什么線程在IO調(diào)用中沒有阻塞時花費(fèi)空閑時間罢屈,通常在此期間在其他通道上執(zhí)行IO嘀韧。也就是說,單個線程現(xiàn)在可以管理多個輸入和輸出通道缠捌。

選擇

Java NIO的選擇器允許單個線程監(jiān)視多個輸入通道锄贷。你可以使用選擇器注冊多個通道,然后使用單個線程“選擇”具有可用于處理的輸入的通道曼月,或者選擇準(zhǔn)備寫入的通道谊却。這種選擇器機(jī)制使單個線程可以輕松管理多個通道。

NIO和IO如何影響應(yīng)用程序設(shè)計

無論你選擇NIO還是IO哑芹,因?yàn)槟愕腎O工具包可能會影響應(yīng)用程序設(shè)計的以下方面:

  1. API調(diào)用NIO或IO類炎辨。
  2. 處理數(shù)據(jù)。
  3. 用于處理數(shù)據(jù)的線程數(shù)绩衷。

API調(diào)用

當(dāng)然蹦魔,使用NIO時的API調(diào)用看起來與使用IO時不同。這并不奇怪咳燕。而不是僅僅從例如InputStream讀取字節(jié)的數(shù)據(jù)字節(jié)勿决,必須首先將數(shù)據(jù)讀入緩沖區(qū),然后從那里進(jìn)行處理招盲。

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

使用純NIO設(shè)計與IO設(shè)計時低缩,數(shù)據(jù)處理也會受到影響。

在IO設(shè)計中,你從InputStream或Reader中讀取字節(jié)的數(shù)據(jù)字節(jié)咆繁。想象一下讳推,你正在處理基于行的文本數(shù)據(jù)流。例如:

姓名:qq
年齡:7歲
電子郵件:qq@mail.com
電話:1234567890

這個文本行流可以像這樣處理:

InputStream input = ...; //從客戶端套接字獲取InputStream

BufferedReader reader = new BufferedReader(new InputStreamReader(input));

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

注意處理狀態(tài)是如何由程序執(zhí)行的程度決定的玩般。換句話說银觅,一旦第一個reader.readLine()方法返回,你就確定已經(jīng)讀取了整行文本坏为。readLine()會阻塞直到讀取整行究驴,這就是原因。你還知道此行包含名稱匀伏。同樣洒忧,當(dāng)?shù)诙€readLine()調(diào)用返回時,你知道此行包含年齡等够颠。

正如你所看到的熙侍,只有當(dāng)有新數(shù)據(jù)要讀取時,程序才會進(jìn)行履磨,并且對于每個步驟蛉抓,你都知道該數(shù)據(jù)是什么。一旦執(zhí)行的線程已經(jīng)超過讀取代碼中的某個數(shù)據(jù)片段蹬耘,該線程就不會在數(shù)據(jù)中向后移動(通常不會)芝雪。此圖中還說明了此原則:

在這里插入圖片描述

|

NIO的實(shí)現(xiàn)看起來會有所不同。這是一個簡化的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

注意第二行從通道讀取字節(jié)到ByteBuffer综苔。當(dāng)該方法調(diào)用返回時惩系,你不知道所需的所有數(shù)據(jù)是否都在緩沖區(qū)內(nèi)。你只知道緩沖區(qū)包含一些字節(jié)如筛。這使得處理更加困難堡牡。

想象一下,在第一次讀妊钆佟(緩沖)調(diào)用之后晤柄,是否所有讀入緩沖區(qū)的內(nèi)容都是半行。例如妖胀,“姓名:An”芥颈。你能處理這些數(shù)據(jù)嗎?并不是的赚抡。在完成任何數(shù)據(jù)的處理之前爬坑,你需要等待至少一整行數(shù)據(jù)進(jìn)入緩沖區(qū)。

那么你怎么知道緩沖區(qū)是否包含足夠的數(shù)據(jù)來處理它涂臣?好吧盾计,你沒有。找出的唯一方法是查看緩沖區(qū)中的數(shù)據(jù)。結(jié)果是署辉,在你知道所有數(shù)據(jù)是否存在之前族铆,你可能需要多次檢查緩沖區(qū)中的數(shù)據(jù)。這既低效又可能在程序設(shè)計方面變得混亂哭尝。例如:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

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

bufferFull()方法必須跟蹤讀入緩沖區(qū)的數(shù)據(jù)量,并返回true或false刚夺,具體取決于緩沖區(qū)是否已滿献丑。換句話說,如果緩沖區(qū)已準(zhǔn)備好進(jìn)行處理侠姑,則認(rèn)為它已滿。

bufferFull()方法掃描緩沖區(qū)箩做,但必須使緩沖區(qū)保持與調(diào)用bufferFull()方法之前相同的狀態(tài)莽红。如果不是,則可能無法在正確的位置讀入讀入緩沖區(qū)的下一個數(shù)據(jù)邦邦。這不是不可能的安吁,但這是另一個需要注意的問題。

如果緩沖區(qū)已滿燃辖,則可以對其進(jìn)行處理鬼店。如果它不滿,你可能能夠部分處理那里的任何數(shù)據(jù)黔龟,如果這在你的特定情況下是有意義的妇智。在許多情況下,它沒有氏身。

這個圖中說明了is-data-in-buffer-ready循環(huán):

在這里插入圖片描述
Java NIO:從通道讀取數(shù)據(jù)巍棱,直到所有需要的數(shù)據(jù)都在緩沖區(qū)中。

摘要

NIO允許你僅使用一個(或幾個)線程來管理多個通道(網(wǎng)絡(luò)連接或文件)蛋欣,但成本是解析數(shù)據(jù)可能比從阻塞流中讀取數(shù)據(jù)時更復(fù)雜航徙。

如果你需要同時管理數(shù)千個打開的連接,每個只發(fā)送一些數(shù)據(jù)陷虎,例如聊天服務(wù)器到踏,在NIO中實(shí)現(xiàn)服務(wù)器可能是一個優(yōu)勢。同樣尚猿,如果你需要與其他計算機(jī)保持大量開放連接窝稿,例如在P2P網(wǎng)絡(luò)中,使用單個線程來管理所有出站連接可能是一個優(yōu)勢谊路。此圖中說明了這一個線程讹躯,多個連接設(shè)計:

在這里插入圖片描述
Java NIO:管理多個連接的單個線程。

如果你擁有較少帶寬的連接,一次發(fā)送大量數(shù)據(jù)潮梯,那么可能最經(jīng)典的IO服務(wù)器實(shí)現(xiàn)可能是最合適的骗灶。此圖說明了經(jīng)典的IO服務(wù)器設(shè)計:

在這里插入圖片描述
Java IO:經(jīng)典的IO服務(wù)器設(shè)計 - 由一個線程處理的一個連接。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秉馏,一起剝皮案震驚了整個濱河市耙旦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萝究,老刑警劉巖免都,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異帆竹,居然都是意外死亡绕娘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門栽连,熙熙樓的掌柜王于貴愁眉苦臉地迎上來险领,“玉大人,你說我怎么就攤上這事秒紧【钅埃” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵熔恢,是天一觀的道長脐湾。 經(jīng)常有香客問我,道長叙淌,這世上最難降的妖魔是什么秤掌? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮凿菩,結(jié)果婚禮上机杜,老公的妹妹穿的比我還像新娘。我一直安慰自己衅谷,他們只是感情好椒拗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著获黔,像睡著了一般蚀苛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玷氏,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天堵未,我揣著相機(jī)與錄音,去河邊找鬼盏触。 笑死渗蟹,一個胖子當(dāng)著我的面吹牛块饺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雌芽,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼授艰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了世落?” 一聲冷哼從身側(cè)響起淮腾,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屉佳,沒想到半個月后谷朝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡武花,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年圆凰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片体箕。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡送朱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出干旁,到底是詐尸還是另有隱情,我是刑警寧澤炮沐,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布争群,位于F島的核電站,受9級特大地震影響大年,放射性物質(zhì)發(fā)生泄漏换薄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一翔试、第九天 我趴在偏房一處隱蔽的房頂上張望轻要。 院中可真熱鬧,春花似錦垦缅、人聲如沸冲泥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凡恍。三九已至,卻和暖如春怔球,著一層夾襖步出監(jiān)牢的瞬間嚼酝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工竟坛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闽巩,地道東北人钧舌。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像涎跨,于是被迫代替她去往敵國和親洼冻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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