【轉】真正理解NIO

原文鏈接:https://www.jianshu.com/p/362b365e1bcc

 

 

前言

高併發量引起的問題

一個使用傳統阻塞I/O的系統,如果還是使用傳統的一個請求對應一個線程這種模式,一旦有高併發的大量請求,就會有如下問題: 

1、線程不夠用, 就算使用了線程池複用線程也無濟於事; 

2、阻塞I/O模式下,會有大量的線程被阻塞,一直在等待數據,這個時候的線程被掛起,只能乾等,CPU利用率很低,換句話說,系統的吞吐量差; 

3、如果網絡I/O堵塞或者有網絡抖動或者網絡故障等,線程的阻塞時間可能很長。整個系統也變的不可靠;

什麼是NIO

java.nio全稱java non-blocking IO(實際上是 new io),是指JDK 1.4 及以上版本里提供的新api(New IO) ,爲所有的原始類型(boolean類型除外)提供緩存支持的數據容器,使用它可以提供非阻塞式的高伸縮性網絡。

HTTP2.0使用了多路複用的技術,做到同一個連接併發處理多個請求,而且併發請求的數量比HTTP1.1大了好幾個數量級。

IO和NIO的區別

原有的 IO 是面向流的、阻塞的,NIO 則是面向塊的、非阻塞的。

怎麼理解IO是面向流的、阻塞的

java1.4以前的io模型,一連接對一個線程。

原始的IO是面向流的,不存在緩存的概念。Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據,需要先將它緩存到一個緩衝區

Java IO的各種流是阻塞的,這意味着當一個線程調用read或 write方法時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入,該線程在此期間不能再幹任何事情了。

阻塞I/O模型

怎麼理解NIO是面向塊的、非阻塞的

NIO是面向緩衝區的。數據讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動,這就增加了處理過程中的靈活性。

Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取,而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此,一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。

通俗理解:NIO是可以做到用一個線程來處理多個操作的。假設有10000個請求過來,根據實際情況,可以分配50或者100個線程來處理。不像之前的阻塞IO那樣,非得分配10000個。

NIO的核心實現

在標準IO API中,你可以操作字節流和字符流,但在新IO中,你可以操作通道和緩衝,數據總是從通道被讀取到緩衝中或者從緩衝寫入到通道中。

NIO核心API Channel, Buffer, Selector

通道Channel

NIO的通道類似於流,但有些區別如下:

1. 通道可以同時進行讀寫,而流只能讀或者只能寫

2. 通道可以實現異步讀寫數據

3. 通道可以從緩衝讀數據,也可以寫數據到緩衝: 

可以從通道讀取數據到緩衝區,也可以把緩衝區的數據寫到通道中

緩存Buffer

緩衝區本質上是一個可以寫入數據的內存塊,然後可以再次讀取,該對象提供了一組方法,可以更輕鬆地使用內存塊,使用緩衝區讀取和寫入數據通常遵循以下四個步驟:

1. 寫數據到緩衝區;

2. 調用buffer.flip()方法;

3. 從緩衝區中讀取數據;

4. 調用buffer.clear()或buffer.compat()方法;

當向buffer寫入數據時,buffer會記錄下寫了多少數據,一旦要讀取數據,需要通過flip()方法將Buffer從寫模式切換到讀模式,在讀模式下可以讀取之前寫入到buffer的所有數據,一旦讀完了所有的數據,就需要清空緩衝區,讓它可以再次被寫入。

Buffer在與Channel交互時,需要一些標誌:

buffer的大小/容量 - Capacity

作爲一個內存塊,Buffer有一個固定的大小值,用參數capacity表示。

當前讀/寫的位置 - Position​

當寫數據到緩衝時,position表示當前待寫入的位置,position最大可爲capacity – 1;當從緩衝讀取數據時,position表示從當前位置讀取。

信息末尾的位置 - limit

在寫模式下,緩衝區的limit表示你最多能往Buffer裏寫多少數據; 寫模式下,limit等於Buffer的capacity,意味着你還能從緩衝區獲取多少數據。

下圖展示了buffer中三個關鍵屬性capacity,position以及limit在讀寫模式中的說明:

buffer中三個關鍵屬性capacity,position以及limit在讀寫模式中的說明

緩衝區常用的操作

向緩衝區寫數據:

    1. 從Channel寫到Buffer;

    2. 通過Buffer的put方法寫到Buffer中;

從緩衝區讀取數據:

    1. 從Buffer中讀取數據到Channel;

    2. 通過Buffer的get方法從Buffer中讀取數據;

flip方法:

     將Buffer從寫模式切換到讀模式,將position值重置爲0,limit的值設置爲之前position的值;

clear方法 vs compact方法:

       clear方法清空緩衝區;compact方法只會清空已讀取的數據,而還未讀取的數據繼續保存在Buffer中;

Selector

一個組件,可以檢測多個NIO channel,看看讀或者寫事件是否就緒。

多個Channel以事件的方式可以註冊到同一個Selector,從而達到用一個線程處理多個請求成爲可能。

一個thread對應多個channel,一個channel處理一個請求。

當你調用Selector的select()或者 selectNow() 方法它只會返回有數據讀取的SelectableChannel的實例.

Thanks for:

學習NIO

美團wiki-NIO

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章