摘要:一前言學(xué)習(xí)了和之后,肯定會問我們到底什么時(shí)候該使用,什么時(shí)候該使用在下文中我會嘗試用例子闡述和的區(qū)別,以及它們對你的設(shè)計(jì)會有什么影響。
一、前言
學(xué)習(xí)了Java IO 和 NIO之后,肯定會問:我們到底什么時(shí)候該使用 IO,什么時(shí)候該使用 NIO?
在下文中我會嘗試用例子闡述java NIO 和IO的區(qū)別,以及它們對你的設(shè)計(jì)會有什么影響。
IO | NIO |
---|---|
面向流(Stream) | 面向緩沖(Buffer) |
阻塞IO | 非阻塞IO |
無 | 選擇器(Selectors) |
Java NIO 和 IO 之間第一個(gè)最大的區(qū)別是,IO是面向流的,NIO是面向緩沖區(qū)的。
Java IO 面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié),直至讀取所有字節(jié),只能順序讀取所有數(shù)據(jù)。如果想要跳過一些字節(jié)或者想要讀取已經(jīng)讀取過的數(shù)據(jù),則必須將從流中的數(shù)據(jù)線緩存起來。
Java NIO的處理方式不一樣。數(shù)據(jù)一開始就被讀寫到緩沖區(qū)(Buffer),根據(jù)需要你可以控制讀取什么位置的數(shù)據(jù)。這就增加了處理過程中的靈活性。然而,你需要額外做的工作是檢查你需要的數(shù)據(jù)是否已經(jīng)全部到了Buffer中,你還需要保證當(dāng)有更多的數(shù)據(jù)進(jìn)入Buffer中時(shí),Buffer中未處理的數(shù)據(jù)不會被覆蓋
2.2 阻塞與非阻塞IOJava IO的各種流是阻塞的。這意味著,當(dāng)一個(gè)線程調(diào)用read() 或 write()時(shí),該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。
Java NIO的非阻塞模式,使一個(gè)線程從某通道發(fā)送請求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時(shí),就什么都不會獲取。而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情。 非阻塞寫也是如此。一個(gè)線程請求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個(gè)線程同時(shí)可以去做別的事情。 線程通常將非阻塞IO的空閑時(shí)間用于在其它通道上執(zhí)行IO操作,所以一個(gè)多帶帶的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道(channel)。
2.3 SelectorsJava NIO 的 Selectors 允許一條線程去監(jiān)控多個(gè) channels 的輸入,你可以向一個(gè) selector 上注冊多個(gè)channel,然后調(diào)用 selector 的 select() 方法判斷是否有新的連接進(jìn)來或者已經(jīng)在 selector 上注冊時(shí)channel 是否有數(shù)據(jù)進(jìn)入。selector 的機(jī)制讓一個(gè)線程管理多個(gè) channel變得簡單。
三、NIO和IO對應(yīng)用的設(shè)計(jì)有何影響選擇使用NIO還是IO做你的IO工具對應(yīng)用主要有以下幾個(gè)方面的影響
1、使用IO和NIO的API是不同的(廢話)
2、處理數(shù)據(jù)的方式
3、處理數(shù)據(jù)所用到的線程數(shù)
當(dāng)然,使用NIO的API調(diào)用時(shí)看起來與使用IO時(shí)有所不同,但這并不意外,因?yàn)椴⒉皇莾H從一個(gè)InputStream逐字節(jié)讀取,而是數(shù)據(jù)必須先讀入緩沖區(qū)再處理。
3.2 處理數(shù)據(jù)的方式在IO的設(shè)計(jì)里,要一個(gè)字節(jié)一個(gè)字節(jié)從InputStream 或者Reader中讀取數(shù)據(jù),想象你正在處理一個(gè)向下面的基于行分割的流
Name:Anna Age: 25 Email: anna@mailserver.com Phone:1234567890
處理文本行的流的代碼應(yīng)該向下面這樣
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();
注意,一旦reader.readLine()方法返回,你就可以確定整行已經(jīng)被讀取,readLine()阻塞知道一整行都被讀取
NIO的實(shí)現(xiàn)會有一些不同,下面是一個(gè)簡單的例子
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer);
注意第二行從channel中讀取數(shù)據(jù)到ByteBuffer,當(dāng)這個(gè)方法返回你不知道是否你需要的所有數(shù)據(jù)都被讀到buffer了,你所知道的一切就是有一些數(shù)據(jù)被讀到了buffer中,但是你并不知道具體有多少數(shù)據(jù),這使程序的處理變得稍微有些困難
想象一下,調(diào)用了read(buffer)方法后,只有半行數(shù)據(jù)被讀進(jìn)了buffer,例如:“Name: An”,你能現(xiàn)在就處理數(shù)據(jù)嗎?當(dāng)然不能。你需要等待直到至少一整行數(shù)據(jù)被讀到buffer中,在這之前確保程序不要處理buffer中的數(shù)據(jù)
你如何知道buffer中是否有足夠的數(shù)據(jù)可以被處理呢?你不知道,唯一的方法就是檢查buffer中的數(shù)據(jù)。可能你會進(jìn)行幾次無效的檢查(檢查了幾次數(shù)據(jù)都不夠進(jìn)行處理),這會令程序設(shè)計(jì)變得比較混亂復(fù)雜
ByteBuffer buffer = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buffer); while(! bufferFull(bytesRead) ) { bytesRead = inChannel.read(buffer); }
bufferFull()方法必須跟蹤有多少數(shù)據(jù)讀入緩沖區(qū),并返回真或假,這取決于緩沖區(qū)是否已滿。換句話說,如果緩沖區(qū)準(zhǔn)備好被處理,那么表示緩沖區(qū)滿了。
bufferFull()方法掃描緩沖區(qū),但必須保持在bufferFull()方法被調(diào)用之前狀態(tài)相同。如果沒有,下一個(gè)讀入緩沖區(qū)的數(shù)據(jù)可能無法讀到正確的位置。這是不可能的,但卻是需要注意的又一問題。
如果緩沖區(qū)已滿,它可以被處理。如果它不滿,并且在你的實(shí)際案例中有意義,你或許能處理其中的部分?jǐn)?shù)據(jù)。但是許多情況下并非如此。下圖展示了“緩沖區(qū)數(shù)據(jù)循環(huán)就緒”:
NIO可讓您只使用一個(gè)(或幾個(gè))單線程管理多個(gè)通道(網(wǎng)絡(luò)連接或文件),但付出的代價(jià)是解析數(shù)據(jù)可能會比從一個(gè)阻塞流中讀取數(shù)據(jù)更復(fù)雜。
如果需要管理同時(shí)打開的成千上萬個(gè)連接,這些連接每次只是發(fā)送少量的數(shù)據(jù),例如聊天服務(wù)器,實(shí)現(xiàn)NIO的服務(wù)器可能是一個(gè)優(yōu)勢。同樣,如果你需要維持許多打開的連接到其他計(jì)算機(jī)上,如P2P網(wǎng)絡(luò)中,使用一個(gè)多帶帶的線程來管理你所有出站連接,可能是一個(gè)優(yōu)勢。一個(gè)線程多個(gè)連接的設(shè)計(jì)方案如下圖所示:
如果你只有少量的連接但是每個(gè)連接都占有很高的帶寬,同時(shí)發(fā)送很多數(shù)據(jù),傳統(tǒng)的IO會更適合。下圖說明了一個(gè)典型的IO服務(wù)器設(shè)計(jì):
Java IO: 一個(gè)典型的IO服務(wù)器設(shè)計(jì)- 一個(gè)連接通過一個(gè)線程處理.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76346.html
摘要:的選擇器允許單個(gè)線程監(jiān)視多個(gè)輸入通道。一旦執(zhí)行的線程已經(jīng)超過讀取代碼中的某個(gè)數(shù)據(jù)片段,該線程就不會在數(shù)據(jù)中向后移動(dòng)通常不會。 1、引言 很多初涉網(wǎng)絡(luò)編程的程序員,在研究Java NIO(即異步IO)和經(jīng)典IO(也就是常說的阻塞式IO)的API時(shí),很快就會發(fā)現(xiàn)一個(gè)問題:我什么時(shí)候應(yīng)該使用經(jīng)典IO,什么時(shí)候應(yīng)該使用NIO? 在本文中,將嘗試用簡明扼要的文字,闡明Java NIO和經(jīng)典IO之...
摘要:而我們現(xiàn)在都已經(jīng)發(fā)布了,的都不知道,這有點(diǎn)說不過去了。而對一個(gè)的讀寫也會有響應(yīng)的描述符,稱為文件描述符,描述符就是一個(gè)數(shù)字,指向內(nèi)核中的一個(gè)結(jié)構(gòu)體文件路徑,數(shù)據(jù)區(qū)等一些屬性。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡單啦 本來我預(yù)想是先來回顧一下傳統(tǒng)的IO模式的,將傳統(tǒng)的IO模式的相關(guān)類理清楚(因?yàn)镮O的類很多)。 但是,發(fā)現(xiàn)在整理的過程已...
摘要:的出現(xiàn)解決了這尷尬的問題,非阻塞模式下,通過,我們的線程只為已就緒的通道工作,不用盲目的重試了。注意要將注冊到,首先需要將設(shè)置為非阻塞模式,否則會拋異常。 showImg(https://segmentfault.com/img/remote/1460000017053374); 背景知識 同步、異步、阻塞、非阻塞 首先,這幾個(gè)概念非常容易搞混淆,但NIO中又有涉及,所以總結(jié)一下。 ...
摘要:的出現(xiàn)解決了這尷尬的問題,非阻塞模式下,通過,我們的線程只為已就緒的通道工作,不用盲目的重試了。注意要將注冊到,首先需要將設(shè)置為非阻塞模式,否則會拋異常。 同步、異步、阻塞、非阻塞首先,這幾個(gè)概念非常容易搞混淆,但NIO中又有涉及,所以總結(jié)一下[1]。 同步:API調(diào)用返回時(shí)調(diào)用者就知道操作的結(jié)果如何了(實(shí)際讀取/寫入了多少字節(jié))。 異步:相對于同步,API調(diào)用返回時(shí)調(diào)用者不知道操作...
摘要:該線程在此期間不能再干任何事情了。線程通訊線程之間通過等方式通訊。選擇器傳統(tǒng)的模式會基于服務(wù)器會為每個(gè)客戶端請求建立一個(gè)線程由該線程單獨(dú)負(fù)貴處理一個(gè)客戶請求。 本文是對NIO知識的歸納與整理 1.阻塞與同步 1)阻塞(Block)和非租塞(NonBlock): 阻塞和非阻塞是進(jìn)程在訪問數(shù)據(jù)的時(shí)候,數(shù)據(jù)是否準(zhǔn)備就緒的一種處理方式,當(dāng)數(shù)據(jù)沒有準(zhǔn)備的時(shí)候阻塞:往往需要等待缞沖區(qū)中的數(shù)據(jù)準(zhǔn)備好...
閱讀 1458·2021-11-22 13:54
閱讀 4380·2021-09-22 15:56
閱讀 1830·2021-09-03 10:30
閱讀 1328·2021-09-03 10:30
閱讀 2094·2019-08-30 15:55
閱讀 1863·2019-08-30 14:13
閱讀 2068·2019-08-29 15:19
閱讀 2375·2019-08-28 18:13