JAVA NIO學習一:NIO簡介、NIO&IO的主要區別


在前面學習了IO之后,今天我們開始進入NIO學習環節,首先我們會NIO做一個簡單的介紹,讓大家認識NIO,然后會和IO進行一個對比認識進行區分。好了,下面我們就開始學習:

一、NIO簡介

1.概述

從JDK1.4開始,Java提供了一系列改進的輸入/輸出處理的新特性,被統稱為NIO(即New I/O)。新增了許多用於處理輸入輸出的類,這些類都被放在java.nio包及子包下,並且對原java.io包中的很多類進行改寫,新增了滿足NIO的功能。NIO采用內存映射文件的方式來處理輸入輸出,NIO將文件或文件的一段區域映射到內存中,這樣就可以像訪問內存一樣訪問文件了。

Java NIO(New IO) 是從Java 1.4版本開始引入的一個新的IO API,可以替代標准的Java IO API。NIO與原來的IO有同樣的作用和目的,但是使用的方式完全不同, NIO支持面向緩沖區的、基於通道的IO操作。 NIO將以更加高效的方式進行文件的讀寫操作。

2.緩沖區Buffer

緩沖區實際上是一個容器對象,更直接的說,其實就是一個數組,在NIO庫中,所有數據都是用緩沖區處理的。在讀取數據時,它是直接讀到緩沖區中的; 在寫入數據時,它也是寫入到緩沖區中的;任何時候訪問 NIO 中的數據,都是將它放到緩沖區中。而在面向流I/O系統中,所有數據都是直接寫入或者直接將數據讀取到Stream對象中。具體看下面這張圖就理解了:

上面的圖描述了從一個客戶端向服務端發送數據,然后服務端接收數據的過程。客戶端發送數據時,必須先將數據存入Buffer中,然后將Buffer中的內容寫入通道。服務端這邊接收數據必須通過Channel將數據讀入到Buffer中,然后再從Buffer中取出數據來處理。

    在NIO中,所有的緩沖區類型都繼承於抽象類Buffer,最常用的就是ByteBuffer,對於Java中的基本類型,基本都有一個具體Buffer類型與之相對應,它們之間的繼承關系如下圖所示:

備注:可以看到出了Boolean 類型外,其它都有對應的Buffer.

3.通道Channel

Channel和傳統IO中的Stream很相似。雖然很相似,但是有很大的區別,主要區別為:通道是雙向的,通過一個Channel既可以進行讀,也可以進行寫;而Stream只能進行單向操作,通過一個Stream只能進行讀或者寫,比如InputStream只能進行讀取操作,OutputStream只能進行寫操作;
通道是一個對象,通過它可以讀取和寫入數據,當然了所有數據都通過Buffer對象來處理。我們永遠不會將字節直接寫入通道中,相反是將數據寫入包含一個或者多個字節的緩沖區。同樣不會直接從通道中讀取字節,而是將數據從通道讀入緩沖區,再從緩沖區獲取這個字節。

比喻:通常我們把IO,比喻成為水流,管道就是水流的通道;NIO中,我們比喻為火車的軌道,然后緩沖區就是上面的火車。
在NIO中,提供了多種通道對象,而所有的通道對象都實現了Channel接口。它們之間的繼承關系如下圖所示:

Channel(通道)表示到實體如硬件設備、文件、網絡套接字或可以執行一個或多個不同I/O操作的程序組件的開放的連接。所有的Channel都不是通過構造器創建的,而是通過傳統的節點InputStream、OutputStream的getChannel方法來返回響應的Channel。

Channel中最常用的三個類方法就是map、read和write,其中map方法用於將Channel對應的部分或全部數據映射成ByteBuffer,而read或write方法有一系列的重載形式,這些方法用於從Buffer中讀取數據或向Buffer中寫入數據。

4.選擇器Selector

Selector類是NIO的核心類,Selector能夠檢測多個注冊的通道上是否有事件發生,如果有事件發生,便獲取事件然后針對每個事件進行相應的響應處理。這樣一來,只是用一個單線程就可以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正有讀寫事件發生時,才會調用函數來進行讀寫,就大大地減少了系統開銷,並且不必為每個連接都創建一個線程,不用去維護多個線程,並且避免了多線程之間的上下文切換導致的開銷。

與Selector有關的一個關鍵類是SelectionKey,一個SelectionKey表示一個到達的事件,這2個類構成了服務端處理業務的關鍵邏輯。

二、NIO 和 IO 區別

1.概述

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

    • Channels and Buffers(通道和緩沖區):標准的IO基於字節流和字符流進行操作的,而NIO是基於通道(Channel)和緩沖區(Buffer)進行操作,數據總是從通道讀取到緩沖區中,或者從緩沖區寫入到通道中。
    • Asynchronous IO(異步IO):Java NIO可以讓你異步的使用IO,例如:當線程從通道讀取數據到緩沖區時,線程還是可以進行其他事情。當數據被寫入到緩沖區時,線程可以繼續處理它。從緩沖區寫入通道也類似。
    • Selectors(選擇器):Java NIO引入了選擇器的概念,選擇器用於監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個的線程可以監聽多個數據通道。

2.使用場景

NIO

  • 優勢在於一個線程管理多個通道;但是數據的處理將會變得復雜;
  • 如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的數據,采用這種;

傳統的IO

  • 適用於一個線程管理一個通道的情況;因為其中的流數據的讀取是阻塞的;
  • 如果需要管理同時打開不太多的連接,這些連接會發送大量的數據;

3.NIO VS IO

NIO vs IO之間的理念上面的區別(NIO將阻塞交給了后台線程執行)

  • IO是面向流的,NIO是面向緩沖區的
    • Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方;
    • NIO則能前后移動流中的數據,因為是面向緩沖區的
  • IO流是阻塞的,NIO流是不阻塞的
    • Java IO的各種流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了
    • Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取。NIO可讓您只使用一個(或幾個)單線程管理多個通道(網絡連接或文件),但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更復雜。 
    • 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。
  • 選擇器
    • Java NIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然后使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已准備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。 

例子比喻:(http://blog.csdn.net/zhouhl_cn/article/details/6568119)

傳統的socket IO中,需要為每個連接創建一個線程,當並發的連接數量非常巨大時,線程所占用的棧內存和CPU線程切換的開銷將非常巨大。使用NIO,不再需要為每個線程創建單獨的線程,可以用一個含有限數量線程的線程池,甚至一個線程來為任意數量的連接服務。由於線程數量小於連接數量,所以每個線程進行IO操作時就不能阻塞,如果阻塞的話,有些連接就得不到處理,NIO提供了這種非阻塞的能力。

小量的線程如何同時為大量連接服務呢,答案就是就緒選擇。這就好比到餐廳吃飯,每來一桌客人,都有一個服務員專門為你服務,從你到餐廳到結帳走人,這樣方式的好處是服務質量好,一對一的服務,VIP啊,可是缺點也很明顯,成本高,如果餐廳生意好,同時來100桌客人,就需要100個服務員,那老板發工資的時候得心痛死了,這就是傳統的一個連接一個線程的方式。

老板是什么人啊,精着呢。這老板就得捉摸怎么能用10個服務員同時為100桌客人服務呢,老板就發現,服務員在為客人服務的過程中並不是一直都忙着,客人點完菜,上完菜,吃着的這段時間,服務員就閑下來了,可是這個服務員還是被這桌客人占用着,不能為別的客人服務,用華為領導的話說,就是工作不飽滿。那怎么把這段閑着的時間利用起來呢。這餐廳老板就想了一個辦法,讓一個服務員(前台)專門負責收集客人的需求,登記下來,比如有客人進來了、客人點菜了,客人要結帳了,都先記錄下來按順序排好。每個服務員到這里領一個需求,比如點菜,就拿着菜單幫客人點菜去了。點好菜以后,服務員馬上回來,領取下一個需求,繼續為別人客人服務去了。這種方式服務質量就不如一對一的服務了,當客人數據很多的時候可能需要等待。但好處也很明顯,由於在客人正吃飯着的時候服務員不用閑着了,服務員這個時間內可以為其他客人服務了,原來10個服務員最多同時為10桌客人服務,現在可能為50桌,60客人服務了。

這種服務方式跟傳統的區別有兩個:

1、增加了一個角色,要有一個專門負責收集客人需求的人。NIO里對應的就是Selector。

2、由阻塞服務方式改為非阻塞服務了,客人吃着的時候服務員不用一直侯在客人旁邊了。傳統的IO操作,比如read(),當沒有數據可讀的時候,線程一直阻塞被占用,直到數據到來。NIO中沒有數據可讀時,read()會立即返回0,線程不會阻塞。

NIO中,客戶端創建一個連接后,先要將連接注冊到Selector,相當於客人進入餐廳后,告訴前台你要用餐,前台會告訴你你的桌號是幾號,然后你就可能到那張桌子坐下了,SelectionKey就是桌號。當某一桌需要服務時,前台就記錄哪一桌需要什么服務,比如1號桌要點菜,2號桌要結帳,服務員從前台取一條記錄,根據記錄提供服務,完了再來取下一條。這樣服務的時間就被最有效的利用起來了。

 

參考資料:

http://www.cnblogs.com/xiaoxi

學習視頻:《尚硅谷》


免責聲明!

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



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