【雜談】對IO與NIO的認識

貓毛·波拿巴發表於2018-11-27

IO流與NIO塊的資料快取

   Java的IO是面向流設計的,通常我們通過IO流讀取資料,只能指定讀取資料的大小,而不能選擇資料讀取的起始位置。資料就像流水一樣,流過我們的應用,一旦流過就無法回頭。除非我們的程式碼對所讀取的資料進行快取,否則就再也見不到它了(針對此次流操作)。

  比如,我們建立一個byte[]陣列來進行資料快取,稍後可根據需要再次讀取陣列內的資料。但是有一點不方便的就是,如果我們要複用一個byte[]陣列,即我們只處理了其中一部分資料,但是想用這個陣列的剩餘空間讀入新資料。那我們就要在程式碼中維護一個偏移量offset,來告訴read操作,讀取到的資料該從哪裡開始存放。

  但是,如果使用NIO的ByteBuffer,就相對來說方便的多,它內部有維護幾個欄位,只要使用者在對其進行讀寫後,正確地呼叫flip()方法,就可以複用緩衝物件。省去了維護偏移量的工作。

  而Netty,進一步完善了緩衝物件,它的ByteBuf物件,內部維護兩個索引(讀寫)。也就不用再呼叫flip()方法了,降低錯誤概率。

注意:BufferedXXStream裡面的緩衝陣列沒有釋出,也就是說,它只在其內部使用,我們外部引用不到。它只是用來減少系統呼叫的。

 IO與NIO的讀寫效率

   據說,在單執行緒場景下,IO的讀寫速度要高於NIO。(未考證)

   NIO的主要優點是,當讀寫條件不滿足的時候,即緩衝區內沒有資料,或沒有空間的時候(這裡的緩衝區指的應該是核心空間)。執行IO操作的執行緒不會被阻塞,所謂阻塞,指的是執行緒是否會被掛起。那麼在多執行緒場景下,我們就可以利用這個優點,即在當讀寫條件滿足的時候再執行IO操作。這樣就省去了一定的等待時間,這段時間內,此執行緒可以做其他事情,如給另一個滿足讀寫條件的Channel進行讀寫。

那我們如何知曉哪些Channel滿足讀寫條件呢?

答案就是Selector,Seletor可以篩選註冊在其上的Channel。可讀,或可寫的Channel將被篩選出來。

那如果有多個Channel同時滿足讀寫條件呢?

那就把滿足條件的Channel,每個都執行一遍。可讀的就讀,可寫的就寫。

那這樣時效性會不會比較差?

是的,會比較差。拿網路IO來講,如果一個執行緒處理多個連線的操作,那麼最好情況就是,這幾個連線,同時讀寫的概率不大,或者讀寫的資料量很小,執行緒處理速度很快,這樣其他讀寫任務就不會等太久。但是,如果,這多個執行緒同時讀寫概率大,或者讀寫資料量大,處理速度較慢,而又比較追求時效性的話,那麼一個執行緒最好不要處理太多連線。

有了NIO,BIO就被放棄了嗎?

BIO就是阻塞IO,那網路IO來講,如果此執行緒呼叫read()操作,且緩衝區無資料可讀的時候,此執行緒將一直阻塞(被掛起),直到有資料可讀,才被喚醒。前面說了,NIO的使用場景是,一個執行緒會維護多個連線,如果多個Channel同時滿足條件,那麼其中最後一個Channel就要等待它前面的Channel完成讀寫操作後,才能開始任務。如果此連線比較追求時效性的話,即要求,自己傳送資料,對方能立即響應並進行處理的話。那麼就可以用BIO進行處理,即用一個專門的執行緒來監聽一個連線,甚至一個連線的讀或寫操作。

個人認為,BIO就像是高速公路上的專用車道,平時用的不多,但是需要的時候,可以立即使用,且不用跟其他車輛競爭。

相關文章