NIO-簡(jiǎn)單入門(mén)
前段時(shí)間在學(xué)習(xí)項(xiàng)目中加入了Dubbo框架,說(shuō)實(shí)話,也就用一下,至于原理,是真的不懂多少。這不,當(dāng)我想要了解Dubbo的原理時(shí),出現(xiàn)了個(gè)陌生詞匯Netty,這下好了,一個(gè)沒(méi)搞懂,又來(lái)一個(gè),那就學(xué)Netty唄,然鵝,是這么簡(jiǎn)單嗎,Netty的底層是NIO,what?啥是NIO鴨,那從這篇開(kāi)始,我就扒一扒NIO那些事,其實(shí)NIO還能往深了扒,什么計(jì)組、操作系統(tǒng)等,奈何我層次不夠,暫且不扒了,等我學(xué)習(xí)下
先來(lái)說(shuō)下傳統(tǒng)IO(BIO)
傳統(tǒng)的IO指的是平常用到的那些輸入流/輸出流、字節(jié)流/字符流等,IO是面向流的,單向的,IO的各種流都是阻塞的,即在進(jìn)行read()和write()操作時(shí),線程直接阻塞,該線程將不能做任何事情;當(dāng)來(lái)了新的請(qǐng)求,就只能重新開(kāi)一個(gè)線程來(lái)處理這個(gè)請(qǐng)求,但也同樣會(huì)阻塞

再說(shuō)下NIO
NIO是在JDK1.4中新出現(xiàn)的內(nèi)容,其作用和IO是一致的,但是實(shí)現(xiàn)方法和作用是不同的,它是面向緩沖區(qū)、雙向操作的、非阻塞的IO

IO和NIO的區(qū)別
| IO | NIO |
| 面向流 | 面向緩沖區(qū) |
| 阻塞IO | 非阻塞IO |
| 無(wú) | Selector選擇器 |
面向流與面向緩沖
IO是面向流進(jìn)行操作的,即每次從流中讀一個(gè)或多個(gè)字節(jié),直到讀取所有的字節(jié),這些數(shù)據(jù)沒(méi)有被緩存起來(lái),需要一次性讀取或?qū)懭耄以诖诉^(guò)程中線程是阻塞狀態(tài);六種的數(shù)據(jù)不能移動(dòng),如果需要移動(dòng),需要將六種的數(shù)據(jù)放到緩沖區(qū)中NIO是面向緩沖區(qū)的,即數(shù)據(jù)是被讀取到緩沖區(qū)中,可以基于緩沖區(qū)對(duì)其中的數(shù)據(jù)進(jìn)行移動(dòng)等操作,但是需要判斷該緩沖區(qū)中是否包含所需的數(shù)據(jù),且需要保證當(dāng)緩沖區(qū)內(nèi)數(shù)據(jù)未處理完成時(shí),不能被新的數(shù)據(jù)覆蓋掉
阻塞IO與非阻塞IO
IO的各種流都是阻塞的,當(dāng)一個(gè)線程調(diào)用流的read()或write()時(shí),該線程會(huì)被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫(xiě)入,期間不能干其他事情,CPU轉(zhuǎn)去處理其他線程,假設(shè)一個(gè)線程監(jiān)聽(tīng)一個(gè)端口,一天只會(huì)有幾次請(qǐng)求進(jìn)來(lái),但是CPU不得不為該線程不斷的做上下文切換,而且大部分切換都是以阻塞告終,這是極其浪費(fèi)系統(tǒng)資源的非阻塞IO,是一個(gè)線程在讀操作時(shí),如果沒(méi)有數(shù)據(jù)可讀,就什么都不會(huì)獲取,而不是阻塞;寫(xiě)操作時(shí),再將數(shù)據(jù)寫(xiě)入某個(gè)通道時(shí),不需要等它完全寫(xiě)入,這個(gè)線程就可以去做其他事情了NIO通信是利用事件驅(qū)動(dòng)機(jī)制,而不是監(jiān)聽(tīng)機(jī)制,事件到了再觸發(fā),NIO線程之間通過(guò)wait、notify等方式通知,保證每次上下文切換都有意義,避免系統(tǒng)資源的浪費(fèi)
選擇器
Java?NIO的選擇器就是將每個(gè)請(qǐng)求通道都注冊(cè)進(jìn)來(lái),然后對(duì)這些通道進(jìn)行監(jiān)視,由專(zhuān)門(mén)的線程來(lái)選擇通道進(jìn)行執(zhí)行,這種選擇機(jī)制,可以使一個(gè)單獨(dú)的線程很容易的來(lái)管理多個(gè)通道
NIO的主要元素
通道(channel):標(biāo)識(shí)打開(kāi)到IO設(shè)備(如:文件、套接字)的鏈接,跟IO中的流差不多,負(fù)責(zé)傳輸
通道的主要實(shí)現(xiàn)類(lèi)FileChannelSocketChannelServerSocketChannelDatagramChannel通道的獲取(1)Java針對(duì)支持通道的類(lèi)提供了getChannel()方法本地IO:FileInputStream/FileOutputStreamRandomAccessFile網(wǎng)絡(luò)IO:SocketServerSocketDatagramSocket(2)在JDK1.7中的NIO.2 針對(duì)各個(gè)通道提供了靜態(tài)方法 open()(3)在JDK1.7中的NIO.2 的Files工具類(lèi)的newByteChannel()
緩沖區(qū)(Buffer):Java NIO中的Buffer用于和NIO通道進(jìn)行交互。數(shù)據(jù)是從通道讀入緩沖區(qū),從緩沖區(qū)寫(xiě)入到通道中的
作用:數(shù)據(jù)的操作主要是在緩沖區(qū)中處理,負(fù)責(zé)存儲(chǔ)結(jié)構(gòu):底層是數(shù)組,用來(lái)存儲(chǔ)不同數(shù)據(jù)類(lèi)型的數(shù)據(jù)分類(lèi):ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBufferAPI::獲取緩沖區(qū)allocateDirect():分配直接緩沖區(qū):存入數(shù)據(jù)到緩沖區(qū)中:獲取緩沖區(qū)中的數(shù)據(jù):切換讀模式reset():恢復(fù)到mark位置:查看緩沖區(qū)中是否還有可操作的數(shù)據(jù):獲取緩沖區(qū)中可操作的數(shù)據(jù):清除已經(jīng)讀過(guò)的數(shù)據(jù):可重復(fù)讀,將position置為0四個(gè)核心屬性::容量,表示緩沖區(qū)中最大存儲(chǔ)數(shù)據(jù)的容量,一旦聲明不能改變limit:界限,表示緩沖區(qū)中可以操作數(shù)據(jù)的大小(limit后面的數(shù)據(jù)不能讀寫(xiě))position:位置,表示緩沖區(qū)中正在操作數(shù)據(jù)的位置mark:位置,標(biāo)記的位置0 < mark < position <= limit <= capacity直接緩沖區(qū)和非直接緩沖區(qū)非直接緩沖區(qū):通過(guò)allocate()方法分配緩沖區(qū),將緩沖區(qū)建立在JVM的內(nèi)存中直接緩沖區(qū):通過(guò)allocateDirect()方法分配直接緩沖區(qū),將緩沖區(qū)建立在物理內(nèi)存中,可以提高效率獲取直接緩沖區(qū)的方式:ByteBuffer.allocateDirect通道內(nèi)存映射文件:FileChannel inChannel = FileChannel.open(...)MappedByteBuffer inMapperBuf = inChannel.map(...)?使用Buffer讀寫(xiě)數(shù)據(jù)一般遵循以下四個(gè)步驟:寫(xiě)入數(shù)據(jù)到Buffer調(diào)用flip()方法從Buffer中讀取數(shù)據(jù)調(diào)用clear()方法或者compact()方法

選擇器(Selector):管理所有的通道,根據(jù)選擇鍵來(lái)對(duì)應(yīng)處理通道
Selector可以監(jiān)聽(tīng)的事件類(lèi)型讀:SelectionKey.OP_READ寫(xiě):SelectionKey.OP_WRITE連接:SelectionKey.OP_CONNECT接收:SelectionKey.OP_ACCEPT監(jiān)聽(tīng)多個(gè)事件可以用?“|”?位或操作符進(jìn)行連接
今天就先說(shuō)到這里,其實(shí)很多概念看下代碼更容易理解,后面的話會(huì)對(duì)內(nèi)容再進(jìn)行補(bǔ)充,包括學(xué)習(xí)下netty,dubbo等原理
由于篇幅問(wèn)題,文中不展示大篇幅的代碼,示例代碼均已上傳到碼云,如需更詳細(xì)的了解,請(qǐng)自行下載代碼進(jìn)行測(cè)試
https://gitee.com/MaYunJerryLee/urmd-code-demo/tree/master/urmd-nio-demo
