NIO三大核心理念
首先介紹NIO中三個核心元件
- Buffer緩沖區
- Channel通道
- Selector選擇器
一、Buffer緩沖區
1.Buffer 三個重要屬性:
- capacity容量:作為一個記憶體塊,Buffer具有一定的固定大小,也成為“容量”
- position位置:寫入模式時代表寫資料的位置。讀取模式時,代表讀取資料的位置
-
limit限制:寫入模式,限制等于buffer的容量。讀取模式下,limit等于寫入的資料量
//注意區分不同模式下的含義。
2. ByteBuffer記憶體類型
-
直接記憶體(direct堆外記憶體):在JVM之外向實體記憶體直接申請記憶體
進行IO或檔案IO時比heapBuffer少一次拷貝
ByteBuffrt directByteBuffer = ByteBuffer.allocateDirect(noBNytes);
優點:
1、進行IO或檔案IO時比heapBuffer少一次拷貝。(file/socket -> OS memory -> jvm heap)
2、在GC範圍之外,降低GC壓力,但實作了自動管理。DirextByteBuffer類提供Cleaner對象(PhantomReference),Cleaner被GC前會執行clean方法,觸發DirectByteBuffer中定義的Deallocator。
- 非直接記憶體(heap堆記憶體):在JVM的申請記憶體
建議:性能确實可觀的時候才去使用;配置設定給大型、長壽命;(網絡傳輸、檔案讀寫場景)
通過虛拟機參數MaxDirectMemorySize限制大小,防止耗盡整個機器記憶體
二、Channel: 通道
和标準的IO Stream差別:在一個通道内進行讀取和寫入時stream通常是單向的(input和output),可以非阻塞讀取和寫入通道,通道始終讀取或寫入緩沖區,channel的API涵蓋了UDP/TCP網絡和檔案IO。(FileCjannel,DatagramCjannel,SocketChannel,ServerSocketChannel)
SocketChannel:用于建立TCP連接配接,類似java.net.Socket.有兩種建立的方式:
1、用戶端主動發起和服務的連接配接。
2、服務端擷取新的連接配接
- SockerChannel:用戶端
//用戶端主動發起連接配接的方式
SocketChannel socketChannel = SocketChannel.open();
//設定為非阻塞模式
socketChannel.configureBlocking(false);
//建立一個端口和ip
socketChannel.connect(new InetSocketAddress(“htttp:163.com”,80));
//發送請求資料 - 向通道寫入資料
socketChannel.write(byteBuffer);
//讀取服務端傳回 - 讀取緩沖區的資料
int butesRead = socketChannel.read(byteBuffer);
//關閉連接配接
socketChannel.close();
write寫:非阻塞,write()在尚未寫入任何内容可能就傳回了。需要在循環中調用
read讀:非阻塞,read()方法可能直接傳回而根本不讀取任何資料,根據傳回的int值判斷讀取了多少位元組
- ServerSocketChannel:服務端
//建立網絡服務端
ServerSocketChannel serverSockerChannel = ServerSocketChannel.open();
//設為非阻塞模式
serverSocletChannel.configureBlocking(false);
//綁定端口
serverSocletChannel.socket().bind(new InetSocketAddress(8080));
while(true){
SocketChannel soccketChannel = serverSocletChannel.accept();
if(socketChannel != null){
//TCP請求 讀取/響應
}
}
注:serverSocletChannel.accept()如果該通道處于非阻塞,如果沒有挂起的連接配接則該方法立即傳回null。
是以必須檢查傳回的對象是否為空
三、Selector:選擇器
- Selector是一個javaNIO 元件,可以檢查一個或多個NIO通道,并确定哪些通道已經準備好進行讀取或寫入。實作單個線程可以管理多個通道,進而管理多個網絡連接配接。核心概念是事件驅動機制。
-
一個線程使用Selector監聽多個channel的不同僚件:
四種事件分别對應SelectionKey四個常量:
Connect:連接配接(Selector.OP_CONNECT) 用戶端與服務端建立連接配接,用戶端觸發事件 Accept: 準備就緒(OP_ACCEPT) 服務端檢測到用戶端連接配接請求,服務端觸發事件 Read: 讀取(OP_READ) 用戶端與服務端建立連接配接之後讀取操作,雙端觸發事件 Write: 寫入(OP_WRITE) 用戶端與服務端建立連接配接之後寫入操作,雙端觸發事件
NIO對比BIO
四、NIO與多線程結合的改進方案
- mainReactor接收->分發給subReactor讀寫->具體業務邏輯分發給單獨的線程池處理
總結
NIO應用于網絡應用開發還是比較繁瑣,要想将性能提升,還需要和多線程技術結合起來。
因為網絡程式設計本身的複雜性,以及JDK API開發的使用難度較高, 是以在開源社群中,湧出
來很多對JDK NIO進行封裝、增強後的網絡程式設計架構,例如:Netty、Mina等。