Java語言發展至今,優勢你們有目共睹:面向對象的語言、簡潔有效、高移植性等等。可是一樣也存在不少缺點,C語言程序員口中Java太慢了,.net程序員口中Java太開放了,php程序員說Java太複雜了。php
Java爲了「一次編寫,處處運行」的最大優點,也付出了相應的代價:html
Java須要運行於虛擬機(即JVM)之上,爲了保證Java字節碼在各類JVM部署平臺上運行效果一致,做些妥協是必須的。既然須要通用於不一樣的操做系統平臺,那麼,某種程度上就必須選擇各類平臺都接受的處理方案。這也就形成了Java不能發揮各類平臺的特性和優化的地方。java
受到這種機制的影響最突出的莫屬I/O領域了。雖然Java也提供了一套比較完備的I/O支持,但都是針對於各類平臺的通用特性。這些I/O類都是面向流數據的操做,適用性普遍,可是當操做大量數據時,致命的效率缺陷也暴露無遺,這就是其餘語言的程序員對JavaI/O的效率嗤之以鼻的緣由。程序員
I/O的終極目標是效率,而效率離不開底層操做系統和文件系統的特性支持。這些特性包括:文件鎖定、非阻塞I/O、就緒性選擇、和內存映射。當今操做系統大都支持這些特性,而Java傳統I/O機制並無模擬這些通用的I/O服務。就好像一個武林高手內功很高,可是卻沒有招式發揮,只能憋着。編程
好在Java語言開發者也意識到了這一點,在Java1.4版本的需求徵集中,其中有一條:Java規範請求#51(JSR 51, http://jcp.org/jsr/detail/51.jsp),包含了對高速、可伸縮I/O特性的詳盡描述,藉助這一特性,底層操做系統的I/O性能能夠獲得更好發揮。緩存
JSR 51的實現也標誌着Java New I/O(NIO)的誕生。其結果就是新增類組合到一塊兒,構成了java.nio及其子包,以及java.util.regex軟件包,同時現存軟件包也相應做了幾處修改。隨着Java1.4版本的發佈,操做系統強大的I/O特性終於能夠藉助Java提供的工具獲得充分發揮。論及I/O性能,Java不再遜於任何一款編程語言。
網絡
傳統Java IO在我前一篇博文《細說Java IO相關》已經介紹過了,它是阻塞的,低效的。那麼Java NIO和傳統Java IO有什麼不一樣?帶來了什麼?數據結構
傳統JavaIO是面向流的I/O。流I/O一次處理一個字節。NIO則是面向塊的I/O,每次操做都是以數據塊爲單位。它們的差距就好象兩我的吃飯,一我的一粒一粒的吃,另外一我的狼吞虎嚥,快慢顯而易見。併發
NIO中引入了緩衝區(Buffer)的概念,緩衝區做爲傳輸數據的基本單位塊,全部對數據的操做都是基於將數據移進/移出緩衝區而來;讀數據的時候從緩衝區中取,寫的時候將數據填入緩衝區。儘管傳統JavaIO中也有相應的緩衝區過濾器流(BufferedInputStream等),可是移進/移出的操做是由程序員來包裝的,它本質是對數據結構化和積累達處處理時的方便,並非一種提升I/O效率的措施。NIO的緩衝區則否則,對緩衝區的移進/移出操做是由底層操做系統來實現的。jsp
一般一次緩衝區操做是這樣的:某個進程須要進行I/O操做,它執行了一次讀(read)或者寫(write)的系統調用,向底層操做系統發出了請求,操做系統會按要求把數據緩衝區填滿或者排幹。提及來簡單,其實很複雜。但至少咱們知道了這事是由操做系統乾的,比咱們代碼級的實現要高效的多。
除了效率上的差異外,緩衝區在數據分析和處理上也帶來的很大的便利和靈活性。
(2)非阻塞的I/O + 就緒性選擇
傳統JavaIO是基於阻塞I/O模型的:當發起一個I/O請求時,若是數據沒有準備好(read時無可讀數據,write時數據不可寫入),那麼線程便會阻塞,直到數據準備好,致使線程大部分的時間都在阻塞。
而非阻塞I/O則容許線程在有數據的時候處理數據,沒有數據的時候乾點別的,提升了資源利用率。
就緒性選擇一般是創建在非阻塞的基礎上,而且更進一步,它把檢查哪些I/O請求的數據準備好這個任務交給了底層操做系統,操做系統會去查看並返回結果集合,這樣咱們只須要關心那些準備好進行操做的IO通道。關於就緒性選擇的過程會在後面詳述。
NIO提供的Socket能夠用非阻塞的方式工做,而且支持就緒性選擇,減小了資源消耗和CPU在線程間的切換,在管理線程效率上比傳統Socket高。
NIO同時帶來了不少當今操做系統大都支持的特性。
文件鎖定是多個進程協同工做的狀況下,要協調進程間對共享數據的訪問必不可少的工具。
內存映射利用虛擬內存技術提供對文件的高速緩存,使讀取磁盤文件就像從內存中讀取同樣高效,可是卻不會有內存泄漏的危險,由於在內存中不會存在文件的完整拷貝。
此外還有一些其餘的特性,後面再詳述。
顯然,使用或者不使用NIO的理由不會是由於技術崇拜,由於這個東西纔出來,看起來很酷我就去使用它。好吧,我認可我是有一點這樣的緣由。
對於文件I/O, 在我看來使用IO和NIO是區別不大的,Java1.4開始原始IO也根據NIO從新實現過了,提供了對於NIO特性的支持。即便是流,也會比之前更加高效。企業級應用軟件中涉及I/O的部分多半是讀寫文件的功能性需求,不多有在併發上的要求,那麼JavaIO包已經很勝任了。
對於網絡I/O,傳統的阻塞式I/O,一個線程對應一個鏈接,採用線程池的模式在大部分場景下簡單高效。當鏈接數茫茫多時,而且數據的移動很是頻繁,NIO無疑是更好的選擇。
NIO標榜的是高速、可伸縮的I/O,由於它更親近操做系統。當需求很平凡,沒有過高的效率要求的時候,你看不出它的好,反而以爲NIO代碼實現複雜,不易理解。選擇與否全看使用的場景,這點就看使用者的權衡了。