nio是java的IO框架里邊十分重要的一部分內容,其最核心的就是提供了非阻塞IO的處理方式,最典型的應用場景就是處理網絡連接。很多同學提起nio都能說起一二,但是細究其背后的原理、思想往往就開始背書,說來說去都是那么幾句,其中不少人并不見的真的很理解。本人之前就屬于此類,看了很多書和博客,但是大多數都只是講了三件套和怎么使用,很少會很細致的講背后的思想,那本次我們就來扒一扒吧。
很多博客描述nio都是這么說的:基于Reactor模式實現的多路非阻塞高性能的網絡IO。那么我們就從這個定義來分析,其中兩個關鍵點:多路非阻塞和Reactor模式。(本來想把高性能也算進去,但是后來想想這個應該算前兩者的結果)下邊我們來分別搞懂這兩塊。
多路非阻塞其實準確的名字叫做IO多路復用模型,其是linux五種網絡模型之一,也是當前網絡編程最常使用的模型之一。至于詳細的介紹請參考博客:高性能IO模型淺析(這個里邊只給出了4中,沒有信號驅動IO,但講的很贊,特別是圖),這里僅作簡要介紹和對比:
對比以上五種模型可以知道,IO復用模型從效率和實現成本綜合而言目前是比較好的選擇,這就是java基于該模型實現nio的根本原因。上邊提到了IO復用模型的實現思想,其實這種思想在其他語言中早已實現(如C++中據說流弊哄哄超10w行代碼的ACE,自適配通信環境,就采用了該模型),并且提出了一個叫Reactor的設計模式。
Reactor模式,翻譯過來叫做反引器模式,其目的是在事件驅動的應用中,將一個請求的能夠分離并且調度給應用程序。我相信大多數人都沒看明白前一句的意思(書還是要背的),說白了就是對于一個請求的多個事件(如連接、讀寫等),經過這種模式的處理,能夠區分出來,并且分別交給對應的處理模塊處理。廢話不多說,來看下一個簡圖:
再來看個簡圖吧:
基本上和Reactor能對應上,少了個dispatcher,這是由于jdk本身提供的nio比較基本,dispatcher一般都由我們自己實現,而在我理解中,mina、netty這些框架很重要的一方面也是提供了該部分的實現。
從《netty權威指南》上抄了個例子以及配圖,而且代碼沒有客戶端的,大家可以瞄一眼吧(為什么沒有?因為已經快一點了,我不想寫了......):
服務器端時序圖:
客戶端時序圖:
服務器端代碼:

package com.gj.netty.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;import java.util.Set;/** * Created by guojing on 2015/6/7. */public class MultiplexerTimerServer implements Runnable { PRivate Selector selector; private ServerSocketChannel servChannel; private volatile boolean stop; public MultiplexerTimerServer(int port) { try { selector = Selector.open(); //新建多路復用selector servChannel = ServerSocketChannel.open(); //新建channel servChannel.configureBlocking(false); //設置非阻塞 servChannel.socket().bind(new InetSocketAddress(port),1024); //端口、塊大小 servChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("TimeServer is start, port:" + port); } catch (IOException e) { e.printStackTrace(); } } public void run() { while (!stop){ try { selector.select(1000); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> ketIt = keys.iterator(); SelectionKey key = null; while (ketIt.hasNext()){ key = ketIt.next(); ketIt.remove(); //處理對應key事件 handler(key); } } catch (IOException e) { e.printStackTrace(); } } } private void handler(SelectionKey key){ //根據key去除channel做對應處理 }}
我想如果這會兒還有人記得標題一定會罵我了,丫的十分鐘個屁啊,認真看完至少待半個小時。這個我只能說如果你之前已經理解了,那么畫個10分鐘瞟一眼無所謂的,如果以前沒理解,如果本文能讓你有了更好的理解,那么花多少時間更無所謂了,要知道懂了java的nio是量的積累,了解了其背后的思想和原理是質的積累。而且,我明明計劃半小時寫完的,這會已經2個多小時過去了......
新聞熱點
疑難解答