原文鏈接: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: