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

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

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

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