IO 和 NIO的區(qū)別鞠眉,NIO優(yōu)點

Java NIO提供了與標準IO不同的IO工作方式:?

Channels and Buffers(通道和緩沖區(qū)):標準的IO基于字節(jié)流和字符流進行操作的,而NIO是基于通道(Channel)和緩沖區(qū)(Buffer)進行操作哗戈,數據總是從通道讀取到緩沖區(qū)中唯咬,或者從緩沖區(qū)寫入到通道中畏浆。

Asynchronous IO(異步IO):Java NIO可以讓你異步的使用IO,例如:當線程從通道讀取數據到緩沖區(qū)時蜀涨,線程還是可以進行其他事情。當數據被寫入到緩沖區(qū)時,線程可以繼續(xù)處理它草娜。從緩沖區(qū)寫入通道也類似痒筒。

Selectors(選擇器):Java NIO引入了選擇器的概念簿透,選擇器用于監(jiān)聽多個通道的事件(比如:連接打開老充,數據到達)啡浊。因此巷嚣,單個的線程可以監(jiān)聽多個數據通道窘拯。

阻塞IO和非阻塞IO

Java IO流都是阻塞的涤姊,這意味著嗤放,當一條線程執(zhí)行read()或者write()方法時,這條線程會一直阻塞直到讀取到了一些數據或者要寫出去的數據已經全部寫出斤吐,在這期間這條線程不能做任何其他的事情搔涝。

java NIO的非阻塞模式(Java NIO有阻塞模式和非阻塞模式和措,阻塞模式的NIO除了使用Buffer存儲數據外和IO基本沒有區(qū)別)允許一條線程從channel中讀取數據,通過返回值來判斷buffer中是否有數據派阱,如果沒有數據,NIO不會阻塞,因為不阻塞這條線程就可以去做其他的事情文兑,過一段時間再回來判斷一下有沒有數據盒刚。NIO的寫也是一樣的,一條線程將buffer中的數據寫入channel因块,它不會等待數據全部寫完才會返回籍铁,而是調用完write()方法就會繼續(xù)向下執(zhí)行

選擇器(Selectors)

?Java NIO的選擇器允許一個單獨的線程來監(jiān)視多個輸入通道拒名,你可以注冊多個通道使用一個選擇器雁佳,然后使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道甘穿。這種選擇機制温兼,使得一個單獨的線程很容易來管理多個通道。

面向流與面向緩沖

Java IO和NIO之間第一個最大的區(qū)別是武契,IO是面向流的募判,NIO是面向緩沖區(qū)的。 Java IO面向流意味著每次從流中讀一個或多個字節(jié)咒唆,直至讀取所有字節(jié)届垫,它們沒有被緩存在任何地方。此外全释,它不能前后移動流中的數據装处。如果需要前后移動從流中讀取的數據,需要先將它緩存到一個緩沖區(qū)浸船。 Java NIO的緩沖導向方法略有不同妄迁。數據讀取到一個它稍后處理的緩沖區(qū),需要時可在緩沖區(qū)中前后移動李命。這就增加了處理過程中的靈活性登淘。但是,還需要檢查是否該緩沖區(qū)中包含所有您需要處理的數據封字。而且黔州,需確保當更多的數據讀入緩沖區(qū)時耍鬓,不要覆蓋緩沖區(qū)里尚未處理的數據。

Java NIO 由以下幾個核心部分組成:

Channels/Buffers/Selectors

?數據可以從Channel讀到Buffer中流妻,也可以從Buffer 寫到Channel中牲蜀。

Channel

Channel的實現(xiàn):(涵蓋了UDP和TCP網絡IO以及文件IO)

FileChannel、DatagramChannel绅这、SocketChannel涣达、ServerSocketChannel

讀數據:int bytesRead = inChannel.read(buf);

寫數據:int bytesWritten = inChannel.write(buf); ?

還有部分的使用,如配置Channel為阻塞或者非阻塞模式君躺,以及如何注冊到Selector上面去峭判,參考Selector部分开缎;

Buffer

Buffer實現(xiàn):(byte,char,short,int,long,float,double)

ByteBuffer棕叫、CharBuffer、DoubleBuffer奕删、FloatBuffer俺泣、IntBuffer、LongBuffer完残、ShortBuffer

Buffer使用:

讀數據:

flip()方法:將Buffer從寫模式切換到讀模式伏钠;調用flip()方法會將position設回0,并將limit設置成之前position的值谨设。

(char) buf.get():讀取數據

rewind():將position設回0熟掂,所以你可以重讀Buffer中的所有數據;limit保持不變扎拣,仍然表示能從Buffer中讀取多少個元素(byte赴肚、char等)。

mark()方法:可以標記Buffer中的一個特定position二蓝。

reset()方法:恢復到Buffer.mark()標記時的position誉券。

一旦讀完了所有的數據,就需要清空緩沖區(qū)刊愚,讓它可以再次被寫入踊跟。

clear()方法:清空整個緩沖區(qū);position將被設回0鸥诽,limit被設置成 capacity的值商玫。

compact()方法:只會清除已經讀過的數據;任何未讀的數據都被移到緩沖區(qū)的起始處牡借,新寫入的數據將放到緩沖區(qū)未讀數據的后面决帖;將position設到最后一個未讀元素正后面,limit被設置成 capacity的值蓖捶。

寫數據

buf.put(127); ?

Buffer的三個屬性:

capacity:含義與模式無關地回;Buffer的一個固定的大小值;Buffer滿了需要將其清空才能再寫;

ByteBuffer.allocate(48)刻像;該buffer的capacity為48byte

CharBuffer.allocate(1024);該buffer的capacity為1024個char?

position:含義取決于Buffer處在讀模式還是寫模式(初始值為0畅买,寫或者讀操作的當前位置)

寫數據時,初始的position值為0细睡;其值最大可為capacity-1

將Buffer從寫模式切換到讀模式谷羞,position會被重置為0

limit:含義取決于Buffer處在讀模式還是寫模式(寫limit=capacity;讀limit等于最多可以讀取到的數據)

寫模式下溜徙,limit等于Buffer的capacity

切換Buffer到讀模式時湃缎, limit表示你最多能讀到多少數據;

Selector

Selector允許單線程處理多個 Channel蠢壹。如果你的應用打開了多個連接(通道)嗓违,但每個連接的流量都很低,使用Selector就會很方便图贸。例如蹂季,在一個聊天服務器中。

要使用Selector疏日,得向Selector注冊Channel偿洁,然后調用它的select()方法。這個方法會一直阻塞到某個注冊的通道有事件就緒沟优。一旦這個方法返回涕滋,線程就可以處理這些事件,事件的例子有如新連接進來挠阁,數據接收等宾肺。?

創(chuàng)建:

Selector selector = Selector.open(); ?

注冊通道:

channel.configureBlocking(false); ?

/*與Selector一起使用時,Channel必須處于非阻塞模式鹃唯,這意味著不能將FileChannel與Selector一起使用爱榕,因為FileChannel不能切換到非阻塞模式(而套接字通道都可以)*/

SelectionKey key = channel.register(selector,?Selectionkey.OP_READ);?

/*第二個參數表明Selector監(jiān)聽Channel時對什么事件感興趣(SelectionKey.OP_CONNECT ?SelectionKey.OP_ACCEPT ?SelectionKey.OP_READ SelectionKey.OP_WRITE),可以用或操作符將多個興趣組合一起*/

SelectionKey

包含了interest集合 、ready集合 坡慌、Channel 黔酥、Selector 、附加的對象(可選)

int interestSet = key.interestOps()洪橘;可以進行類似interestSet & SelectionKey.OP_CONNECT的判斷

使用:

select():阻塞到至少有一個通道在你注冊的事件上就緒了

selectNow():不會阻塞跪者,不管什么通道就緒都立刻返回

selectedKeys():訪問“已選擇鍵集(selected key set)”中的就緒通道

close():使用完selector需要用其close()方法會關閉該Selector,且使注冊到該Selector上的所有SelectionKey實例無效

Set?selectedKeys?=?selector.selectedKeys();?

?Iterator?keyIterator?=?selectedKeys.iterator();??

while(keyIterator.hasNext())?{??????

????SelectionKey?key?=?keyIterator.next();?????

?????if(key.isAcceptable())?{?????????

?????????//?a?connection?was?accepted?by?a?ServerSocketChannel.?????

?????}?else?if?(key.isConnectable())?{??????????

????????//?a?connection?was?established?with?a?remote?server.??????

????}?else?if?(key.isReadable())?{??????????

????????//?a?channel?is?ready?for?reading??????

????}?else?if?(key.isWritable())?{??????????

????????//?a?channel?is?ready?for?writing?????

?????}?? ?

????keyIterator.remove();//注意這里必須手動remove熄求;表明該selectkey我已經處理過了渣玲;

}



Java測試關鍵代碼

RandomAccessFile?aFile?=newRandomAccessFile("data/nio-data.txt","rw");

FileChannel?inChannel?=?aFile.getChannel(); ?//從一個InputStream outputstream中獲取channel

//create?buffer?with?capacity?of?48?bytes

ByteBuffer?buf?=?ByteBuffer.allocate(48);

int?bytesRead?=?inChannel.read(buf);//read?into?buffer.

while(bytesRead?!=?-1)?{

????buf.flip();//make?buffer?ready?for?read

? ??while(buf.hasRemaining()){

????????System.out.print((char)?buf.get());//?read?1?byte?at?a?time

??}??

????buf.clear();//make?buffer?ready?for?writing

? ? bytesRead?=?inChannel.read(buf);??

}??

aFile.close(); ?

文件通道

RandomAccessFile?aFile?=newRandomAccessFile("data/nio-data.txt","rw");

FileChannel?inChannel?=?aFile.getChannel(); ?

讀數據

ByteBuffer?buf?=?ByteBuffer.allocate(48);

int?bytesRead?=?inChannel.read(buf);

寫數據

String?newData?="New?String?to?write?to?file..."+?System.currentTimeMillis();?

ByteBuffer?buf?=?ByteBuffer.allocate(48);

buf.clear();??

buf.put(newData.getBytes()); ?

buf.flip(); ?

while(buf.hasRemaining())?{

????channel.write(buf);??

}?

Socket 通道

SocketChannel?socketChannel?=?SocketChannel.open();??

socketChannel.connect(newInetSocketAddress("http://jenkov.com",80));

讀數據

ByteBuffer?buf?=?ByteBuffer.allocate(48);

int?bytesRead?=?socketChannel.read(buf);

寫數據

String?newData?="New?String?to?write?to?file..."+?System.currentTimeMillis();

ByteBuffer?buf?=?ByteBuffer.allocate(48);

buf.clear();??

buf.put(newData.getBytes()); ?

buf.flip(); ?

while(buf.hasRemaining())?{

????socketChannel.write(buf);??

} ?

ServerSocket 通道

ServerSocketChannel?serverSocketChannel?=?ServerSocketChannel.open(); ?

serverSocketChannel.socket().bind(newInetSocketAddress(9999));

while(true){

????SocketChannel?socketChannel?=??

????????????serverSocketChannel.accept(); ?

//do?something?with?socketChannel...

}?

Datagram通道(channel 的讀寫操作與前面的有差異)

DatagramChannel?channel?=?DatagramChannel.open();??

channel.socket().bind(newInetSocketAddress(9999));

讀數據

ByteBuffer?buf?=?ByteBuffer.allocate(48);

buf.clear();??

channel.receive(buf);

/*receive()方法會將接收到的數據包內容復制到指定的Buffer. 如果Buffer容不下收到的數據,多出的數據將被丟棄弟晚。 */

寫數據

String?newData?="New?String?to?write?to?file..."+?System.currentTimeMillis();

ByteBuffer?buf?=?ByteBuffer.allocate(48);

buf.clear();??

buf.put(newData.getBytes());??

buf.flip(); ?

int?bytesSent?=?channel.send(buf,newInetSocketAddress("jenkov.com",80));


本文部分轉自:

Java NIO 與 IO之間的區(qū)別

Java NIO:IO與NIO的區(qū)別


個人公號:【排骨肉段】忘衍,可以關注一下逾苫。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市枚钓,隨后出現(xiàn)的幾起案子铅搓,更是在濱河造成了極大的恐慌,老刑警劉巖搀捷,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件星掰,死亡現(xiàn)場離奇詭異,居然都是意外死亡嫩舟,警方通過查閱死者的電腦和手機氢烘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來家厌,“玉大人播玖,你說我怎么就攤上這事∠窠郑” “怎么了黎棠?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵晋渺,是天一觀的道長镰绎。 經常有香客問我,道長木西,這世上最難降的妖魔是什么畴栖? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮八千,結果婚禮上吗讶,老公的妹妹穿的比我還像新娘。我一直安慰自己恋捆,他們只是感情好照皆,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沸停,像睡著了一般膜毁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愤钾,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天瘟滨,我揣著相機與錄音,去河邊找鬼能颁。 笑死杂瘸,一個胖子當著我的面吹牛,可吹牛的內容都是我干的伙菊。 我是一名探鬼主播败玉,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼敌土,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了运翼?” 一聲冷哼從身側響起纯赎,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎南蹂,沒想到半個月后犬金,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡六剥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年晚顷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疗疟。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡该默,死狀恐怖,靈堂內的尸體忽然破棺而出策彤,到底是詐尸還是另有隱情栓袖,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布店诗,位于F島的核電站裹刮,受9級特大地震影響,放射性物質發(fā)生泄漏庞瘸。R本人自食惡果不足惜捧弃,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望擦囊。 院中可真熱鬧违霞,春花似錦、人聲如沸瞬场。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贯被。三九已至眼五,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刃榨,已是汗流浹背弹砚。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枢希,地道東北人桌吃。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像苞轿,于是被迫代替她去往敵國和親茅诱。 傳聞我的和親對象是個殘疾皇子逗物,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容