Java NIO(1):遲遲登場的NIO


Java NIO的出現  

  Java語言發展至今,優點大家有目共睹:面向對象的語言、簡潔有效、高移植性等等。但是同樣也存在很多缺點,C語言程序員口中Java太慢了,.net程序員口中Java太開放了,php程序員說Java太復雜了。

  Java為了“一次編寫,到處運行”的最大優勢,也付出了相應的代價:

  Java需要運行於虛擬機(即JVM)之上,為了保證Java字節碼在各種JVM部署平台上運行效果一致,作些妥協是必須的。既然需要通用於不同的操作系統平台,那么,某種程度上就必須選擇各種平台都接受的處理方案。這也就造成了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 NIO帶來了什么?

  傳統Java IO在我前一篇博文細說Java IO相關已經介紹過了,它是阻塞的,低效的。那么Java NIO和傳統Java IO有什么不同?帶來了什么?

(1)面向塊的I/O

  傳統JavaIO是面向流的I/O。流I/O一次處理一個字節。NIO則是面向塊的I/O,每次操作都是以數據塊為單位。它們的差距就好象兩個人吃飯,一個人一粒一粒的吃,另一個人狼吞虎咽,快慢顯而易見。

  NIO中引入了緩沖區(Buffer)的概念,緩沖區作為傳輸數據的基本單位塊,所有對數據的操作都是基於將數據移進/移出緩沖區而來;讀數據的時候從緩沖區中取,寫的時候將數據填入緩沖區。盡管傳統JavaIO中也有相應的緩沖區過濾器流(BufferedInputStream等),但是移進/移出的操作是由程序員來包裝的,它本質是對數據結構化和積累達到處理時的方便,並不是一種提高I/O效率的措施。NIO的緩沖區則不然,對緩沖區的移進/移出操作是由底層操作系統來實現的。

  通常一次緩沖區操作是這樣的:某個進程需要進行I/O操作,它執行了一次讀(read)或者寫(write)的系統調用,向底層操作系統發出了請求,操作系統會按要求把數據緩沖區填滿或者排干。說起來簡單,其實很復雜。但至少我們知道了這事是由操作系統干的,比我們代碼級的實現要高效的多。

  除了效率上的差別外,緩沖區在數據分析和處理上也帶來的很大的便利和靈活性。

 (2)非阻塞的I/O + 就緒性選擇

  傳統JavaIO是基於阻塞I/O模型的:當發起一個I/O請求時,如果數據沒有准備好(read時無可讀數據,write時數據不可寫入),那么線程便會阻塞,直到數據准備好,導致線程大部分的時間都在阻塞。

  而非阻塞I/O則允許線程在有數據的時候處理數據,沒有數據的時候干點別的,提高了資源利用率。

  就緒性選擇通常是建立在非阻塞的基礎上,並且更進一步,它把檢查哪些I/O請求的數據准備好這個任務交給了底層操作系統,操作系統會去查看並返回結果集合,這樣我們只需要關心那些准備好進行操作的IO通道。關於就緒性選擇的過程會在后面詳述。

  NIO提供的Socket可以用非阻塞的方式工作,並且支持就緒性選擇,減少了資源消耗和CPU在線程間的切換,在管理線程效率上比傳統Socket高。

(3)文件鎖定和內存映射文件等操作系統特性

  NIO同時帶來了很多當今操作系統大都支持的特性。

  文件鎖定是多個進程協同工作的情況下,要協調進程間對共享數據的訪問必不可少的工具。

  內存映射利用虛擬內存技術提供對文件的高速緩存,使讀取磁盤文件就像從內存中讀取一樣高效,但是卻不會有內存泄漏的危險,因為在內存中不會存在文件的完整拷貝。

  此外還有一些其他的特性,后面再詳述。

 

為什么要使用NIO?

  顯然,使用或者不使用NIO的理由不會是因為技術崇拜,因為這個東西才出來,看起來很酷我就去使用它。好吧,我承認我是有一點這樣的原因。

  對於文件I/O, 在我看來使用IO和NIO是區別不大的,Java1.4開始原始IO也根據NIO重新實現過了,提供了對於NIO特性的支持。即使是流,也會比以前更加高效。企業級應用軟件中涉及I/O的部分多半是讀寫文件的功能性需求,很少有在並發上的要求,那么JavaIO包已經很勝任了。

  對於網絡I/O,傳統的阻塞式I/O,一個線程對應一個連接,采用線程池的模式在大部分場景下簡單高效。當連接數茫茫多時,並且數據的移動非常頻繁,NIO無疑是更好的選擇。

  NIO標榜的是高速、可伸縮的I/O,因為它更親近操作系統。當需求很平凡,沒有太高的效率要求的時候,你看不出它的好,反而覺得NIO代碼實現復雜,不易理解。選擇與否全看使用的場景,這點就看使用者的權衡了。

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM