總的來講,java中的IO和NIO主要有三點區別:java
IO | NIO |
面向流 | 面向緩衝 |
阻塞IO | 非阻塞IO |
無 | 選擇器(Selectors) |
1.面向流與面向緩衝
Java NIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。 Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取全部字節,它們沒有被緩存在任何地方。此外,它不能先後移動流中的數據。若是須要先後移動從流中讀取的數據,須要先將它緩存到一個緩衝區。 Java NIO的緩衝導向方法略有不一樣。數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動。這就增長了處理過程當中的靈活性。可是,還須要檢查是否該緩衝區中包含全部您須要處理的數據。並且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏還沒有處理的數據。緩存
2.阻塞與非阻塞IO
Java IO的各類流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據徹底寫入。該線程在此期間不能再幹任何事情了。 Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,可是它僅能獲得目前可用的數據,若是目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻塞,因此直至數據變的能夠讀取以前,該線程能夠繼續作其餘的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不須要等待它徹底寫入,這個線程同時能夠去作別的事情。 線程一般將非阻塞IO的空閒時間用於在其它通道上執行IO操做,因此一個單獨的線程如今能夠管理多個輸入和輸出通道(channel)。併發
3.選擇器(Selectors)
Java NIO的選擇器容許一個單獨的線程來監視多個輸入通道,你能夠註冊多個通道使用一個選擇器,而後使用一個單獨的線程來「選擇」通道:這些通道里已經有能夠處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。socket
下面着重談一下阻塞和非阻塞各自的優點:函數
一般的,對一個文件描述符指定的文件或設備, 有兩種工做方式: 阻塞與非阻塞。所謂阻塞方式的意思是指, 當試圖對該文件描述符進行讀寫時, 若是當時沒有東西可讀,或者暫時不可寫, 程序就進入等待狀態, 直到有東西可讀或者可寫爲止。而對於非阻塞狀態, 若是沒有東西可讀, 或者不可寫, 讀寫函數立刻返回, 而不會等待。spa
傳統的socket IO中,須要爲每一個鏈接建立一個線程,當併發的鏈接數量很是巨大時,線程所佔用的棧內存和CPU線程切換的開銷將很是巨大。這種方式具備很高的響應速度,而且控制起來也很簡單,在鏈接數較少的時候很是有效,可是若是對每個鏈接都產生一個線程的無疑是對系統資源的一種浪費,若是鏈接數較多將會出現資源不足的狀況。線程
使用NIO,再也不須要爲每一個線程建立單獨的線程,能夠用一個含有限數量線程的線程池,甚至一個線程來爲任意數量的鏈接服務。因爲線程數量小於鏈接數量,因此每一個線程進行IO操做時就不能阻塞,若是阻塞的話,有些鏈接就得不處處理,NIO提供了這種非阻塞的能力。設計
小量的線程如何同時爲大量鏈接服務呢,答案就是就緒選擇。這就比如到餐廳吃飯,每來一桌客人,都有一個服務員專門爲你服務,從你到餐廳到結賬走人,這樣方式的好處是服務質量好,一對一的服務,VIP啊,但是缺點也很明顯,成本高,若是餐廳生意好,同時來100桌客人,就須要100個服務員,那老闆發工資的時候得心痛死了,這就是傳統的一個鏈接一個線程的方式。事件
老闆是什麼人啊,精着呢。這老闆就得捉摸怎麼能用10個服務員同時爲100桌客人服務呢,老闆就發現,服務員在爲客人服務的過程當中並非一直都忙着,客人點完菜,上完菜,吃着的這段時間,服務員就閒下來了,但是這個服務員仍是被這桌客人佔用着,不能爲別的客人服務,用華爲領導的話說,就是工做不飽滿。那怎麼把這段閒着的時間利用起來呢。這餐廳老闆就想了一個辦法,讓一個服務員(前臺)專門負責收集客人的需求,登記下來,好比有客人進來了、客人點菜了,客人要結賬了,都先記錄下來按順序排好。每一個服務員到這裏領一個需求,好比點菜,就拿着菜單幫客人點菜去了。點好菜之後,服務員立刻回來,領取下一個需求,繼續爲別人客人服務去了。這種方式服務質量就不如一對一的服務了,當客人數據不少的時候可能須要等待。但好處也很明顯,因爲在客人正吃飯着的時候服務員不用閒着了,服務員這個時間內能夠爲其餘客人服務了,原來10個服務員最多同時爲10桌客人服務,如今可能爲50桌,60客人服務了。內存
這種服務方式跟傳統的區別有兩個:
一、增長了一個角色,要有一個專門負責收集客人需求的人。NIO裏對應的就是Selector。
二、由阻塞服務方式改成非阻塞服務了,客人吃着的時候服務員不用一直侯在客人旁邊了。傳統的IO操做,好比read(),當沒有數據可讀的時候,線程一直阻塞被佔用,直到數據到來。NIO中沒有數據可讀時,read()會當即返回0,線程不會阻塞。
NIO 設計背後的基石:反應器(Reactor)模式,用於事件多路分離和分派的體系結構模式。NIO中,客戶端建立一個鏈接後,先要將鏈接註冊到Selector,至關於客人進入餐廳後,告訴前臺你要用餐,前臺會告訴你你的桌號是幾號,而後你就可能到那張桌子坐下了,SelectionKey就是桌號。當某一桌須要服務時,前臺就記錄哪一桌須要什麼服務,好比1號桌要點菜,2號桌要結賬,服務員從前臺取一條記錄,根據記錄提供服務,完了再來取下一條。這樣服務的時間就被最有效的利用起來了。