前言
高并發(fā)量引起的問題
一個使用傳統(tǒng)阻塞I/O的系統(tǒng),如果還是使用傳統(tǒng)的一個請求對應一個線程這種模式,一旦有高并發(fā)的大量請求,就會有如下問題:?
1、線程不夠用, 就算使用了線程池復用線程也無濟于事;?
2筹陵、阻塞I/O模式下,會有大量的線程被阻塞,一直在等待數(shù)據(jù),這個時候的線程被掛起,只能干等,CPU利用率很低,換句話說,系統(tǒng)的吞吐量差;?
3刽锤、如果網(wǎng)絡I/O堵塞或者有網(wǎng)絡抖動或者網(wǎng)絡故障等,線程的阻塞時間可能很長。整個系統(tǒng)也變的不可靠;
什么是NIO
java.nio全稱java non-blocking IO(實際上是 new io)朦佩,是指JDK 1.4 及以上版本里提供的新api(New IO) 并思,為所有的原始類型(boolean類型除外)提供緩存支持的數(shù)據(jù)容器,使用它可以提供非阻塞式的高伸縮性網(wǎng)絡语稠。
HTTP2.0使用了多路復用的技術宋彼,做到同一個連接并發(fā)處理多個請求,而且并發(fā)請求的數(shù)量比HTTP1.1大了好幾個數(shù)量級仙畦。
IO和NIO的區(qū)別
原有的 IO 是面向流的输涕、阻塞的,NIO 則是面向塊的慨畸、非阻塞的莱坎。
怎么理解IO是面向流的、阻塞的
java1.4以前的io模型寸士,一連接對一個線程檐什。
原始的IO是面向流的,不存在緩存的概念碉京。Java IO面向流意味著每次從流中讀一個或多個字節(jié)厢汹,直至讀取所有字節(jié),它們沒有被緩存在任何地方谐宙。此外,它不能前后移動流中的數(shù)據(jù)界弧。如果需要前后移動從流中讀取的數(shù)據(jù)凡蜻,需要先將它緩存到一個緩沖區(qū)
Java IO的各種流是阻塞的,這意味著當一個線程調用read或 write方法時垢箕,該線程被阻塞划栓,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入条获,該線程在此期間不能再干任何事情了忠荞。
怎么理解NIO是面向塊的、非阻塞的
NIO是面向緩沖區(qū)的。數(shù)據(jù)讀取到一個它稍后處理的緩沖區(qū)委煤,需要時可在緩沖區(qū)中前后移動堂油,這就增加了處理過程中的靈活性。
Java NIO的非阻塞模式碧绞,使一個線程從某通道發(fā)送請求讀取數(shù)據(jù)府框,但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時讥邻,就什么都不會獲取迫靖,而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前兴使,該線程可以繼續(xù)做其他的事情系宜。 非阻塞寫也是如此,一個線程請求寫入一些數(shù)據(jù)到某通道发魄,但不需要等待它完全寫入盹牧,這個線程同時可以去做別的事情。
通俗理解:NIO是可以做到用一個線程來處理多個操作的欠母。假設有10000個請求過來,根據(jù)實際情況欢策,可以分配50或者100個線程來處理。不像之前的阻塞IO那樣赏淌,非得分配10000個踩寇。
NIO的核心實現(xiàn)
在標準IO API中,你可以操作字節(jié)流和字符流六水,但在新IO中俺孙,你可以操作通道和緩沖,數(shù)據(jù)總是從通道被讀取到緩沖中或者從緩沖寫入到通道中掷贾。
NIO核心API Channel, Buffer, Selector
通道Channel
NIO的通道類似于流睛榄,但有些區(qū)別如下:
1. 通道可以同時進行讀寫,而流只能讀或者只能寫
2. 通道可以實現(xiàn)異步讀寫數(shù)據(jù)
3. 通道可以從緩沖讀數(shù)據(jù)想帅,也可以寫數(shù)據(jù)到緩沖:?
緩存Buffer
緩沖區(qū)本質上是一個可以寫入數(shù)據(jù)的內存塊,然后可以再次讀取港准,該對象提供了一組方法旨剥,可以更輕松地使用內存塊,使用緩沖區(qū)讀取和寫入數(shù)據(jù)通常遵循以下四個步驟:
1. 寫數(shù)據(jù)到緩沖區(qū)浅缸;
2. 調用buffer.flip()方法轨帜;
3. 從緩沖區(qū)中讀取數(shù)據(jù);
4. 調用buffer.clear()或buffer.compat()方法衩椒;
當向buffer寫入數(shù)據(jù)時蚌父,buffer會記錄下寫了多少數(shù)據(jù)哮兰,一旦要讀取數(shù)據(jù),需要通過flip()方法將Buffer從寫模式切換到讀模式苟弛,在讀模式下可以讀取之前寫入到buffer的所有數(shù)據(jù)喝滞,一旦讀完了所有的數(shù)據(jù),就需要清空緩沖區(qū)嗡午,讓它可以再次被寫入囤躁。
Buffer在與Channel交互時,需要一些標志:
buffer的大小/容量 -?Capacity
作為一個內存塊荔睹,Buffer有一個固定的大小值狸演,用參數(shù)capacity表示。
當前讀/寫的位置 -?Position?
當寫數(shù)據(jù)到緩沖時僻他,position表示當前待寫入的位置宵距,position最大可為capacity – 1;當從緩沖讀取數(shù)據(jù)時吨拗,position表示從當前位置讀取满哪。
信息末尾的位置 -?limit
在寫模式下,緩沖區(qū)的limit表示你最多能往Buffer里寫多少數(shù)據(jù)劝篷; 寫模式下哨鸭,limit等于Buffer的capacity,意味著你還能從緩沖區(qū)獲取多少數(shù)據(jù)娇妓。
下圖展示了buffer中三個關鍵屬性capacity像鸡,position以及l(fā)imit在讀寫模式中的說明:
緩沖區(qū)常用的操作
向緩沖區(qū)寫數(shù)據(jù):
? ? 1. 從Channel寫到Buffer哈恰;
? ? 2. 通過Buffer的put方法寫到Buffer中只估;
從緩沖區(qū)讀取數(shù)據(jù):
? ? 1. 從Buffer中讀取數(shù)據(jù)到Channel;
? ? 2. 通過Buffer的get方法從Buffer中讀取數(shù)據(jù)着绷;
flip方法:
? ? ?將Buffer從寫模式切換到讀模式蛔钙,將position值重置為0,limit的值設置為之前position的值荠医;
clear方法 vs compact方法:
? ? ? ?clear方法清空緩沖區(qū)吁脱;compact方法只會清空已讀取的數(shù)據(jù),而還未讀取的數(shù)據(jù)繼續(xù)保存在Buffer中彬向;
Selector
一個組件豫喧,可以檢測多個NIO channel,看看讀或者寫事件是否就緒幢泼。
多個Channel以事件的方式可以注冊到同一個Selector,從而達到用一個線程處理多個請求成為可能讲衫。
Thanks for: