粗暴的解釋程序員
從JDK 1.0開始,用戶就能夠經過Socket進行網絡編程。在JDK 1.4後,又提供了一種全新的編程方式。編程
在翻閱文檔的時候,相信很多人對「多路複用」這個詞感到陌生和費解。服務器
對於程序員來講,代碼是更容易理解。網絡
BIO示例:多線程
ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//創建線程池 ServerSocket serverSocket = new ServerSocket();//新建Socket serverSocket.bind(8088);//監聽8088端口 while(!Thread.currentThread.isInturrupted()){ Socket socket = serverSocket.accept();//循環等待新鏈接進入 executor.submit(new ConnectIOnHandler(socket));//放入線程池中處理讀寫任務 } class ConnectIOnHandler extends Thread{ private Socket socket; public ConnectIOnHandler(Socket socket){ this.socket = socket; } public void run(){ while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){ String someThing = socket.read();//讀取對方發來的消息 if(someThing!=null){ //TODO 作業務處理 socket.write();//返回結果 } } } }
NIO 示例:併發
class IoThread extends Thread{ public void run(){ Set<SelectionKey> selectedKeys= selector.selectedKeys(); //循環獲取網絡事件。 Iterator<SelectionKey> i = selectedKeys.iterator(); for (;;) { final SelectionKey k = i.next(); final Object a = k.attachment(); i.remove(); if (a instanceof AbstractNioChannel) { processSelectedKey(k, (AbstractNioChannel) a); } }
代碼分析socket
在OIO中,將新鏈接放入到線程池中,利用多線程處理實際的讀寫任務。其中Socket的accept、read、write皆爲阻塞操做。能夠簡單的認爲每個Socket鏈接對應了操做系統的一個小文件,對Socket的讀寫就是對這個文件的讀寫。這是一路一線程,或者是多路一線程的概念。性能
而在NIO中,經過Selector將Channel和event統一管理了起來。一次selector操做能夠獲取一個或者多個網絡鏈接中的讀寫事件。這即是是多路複用的由來,它能夠同時對多個網絡鏈接進行操做。this
爲什麼引入NIO操作系統
在OIO編程模式中,經過線程池的引入,解決了OIO阻塞而引起的並行效率問題。看似已經美好,可是互聯網的發展和用戶量的提高,這種古老的編程模式遭受了挑戰。
在OIO中,很難提高併發量。每個新鏈接都須要一個Socket對象,雖然共用了線程池,可是爲了保證響應性,須要設置較大的線程數。因爲線程資源自己昂貴,過量的線程又會致使CPU資源競爭激烈,下降服務性能。OIO的特性決定了它必然沒法支撐較大的併發量。
在NIO中,經過多路複用機制,解決了須要很是多的線程專一IO讀寫的問題,下降了服務的負載。從而提高了服務器的併發量。
多路複用使得服務的併發性上升了一個臺階,進一步的提高還有一個挑戰。下一章將對此進行詳細闡述。