NIO線程模型
什么是NIO線程模型?
上圖是NIO的線程模型, 基於select實現, 這種線程模型的特點: 多條channel通過一個選擇器和單挑線程綁定, 並且在這種編程模型中, Channel中相關業務邏輯不允許存在耗時的任務 , 如果一定會有耗時的邏輯, 請將它們放置到線程池中去運行, 因為這種模型雖然做到了非阻塞, 但是他並不是真正的異步編程, 任何channel上的任何耗時的操作, 都會拖垮這個選擇器, 進而拖垮整條線程 , 這也是為啥它會被稱為 同步非阻塞
什么是同步?
- 其一: 因為當channel中出現了耗時的操作時, 其他的channel不得不同步等待
- 其二: 從編碼上看: NIO編程中 服務端的
select()
會同步等待選擇器感興趣的事件發生 - 其三: 從操作系統的角度上看, 程序使用的數據來自 網卡 -> 操作系統的內核緩沖區 -> 用戶區, 當數據進入用戶區后java程序便可以對其進行讀寫操作, 所謂同步就是: 數據進入用戶區的過程中,NIO編程模型需要同步並不停的詢問
NIO線程模型的優點
NIO線程線程模型相對於傳統的BIO來說, 最大的優勢就是在於 NIO線程模型中單條線程可同時為N個用戶(Channel)服務, 而BIO編程模型讓人詬病的地方就是, 任何一個新連接接入, 服務器都得為他開啟不止一條新的線程去運行它, 這種BIO系統中, 並發肯定不會很高
NIO適用場景:
NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,並發局限於應用中,編程比較復雜,JDK1.4開始支持
AIO (Asynchronous Input/Output )模型
什么是AIO?
AIO是(jdk1.7) 發行的 異步IO編程模型, 真正實現了異步IO, 基於Linux系統的 Epoll 機制實現
無論是NIO, 還是AIO底層都沒有改變網絡通信的基本步驟, 而是在這個基礎上進行了一系列的升級
AIO的底層實現是由操作系統完成的, 數據在內核空間&用戶空間的遷移, 我們在編寫代碼時也是這樣, 只需要調用 AIO.read()
或者是 AIO.write()
即可, 換句話說, 我們的業務邏輯就成了 回調, 原來在操作系統處理數據的這個過程中, 我們的程序需要阻塞等待着, 亦或者放在線程池中運行, 而在AIO編程中這段等待時間差被省去了, 因為當操作系統認為數據還有沒准備完時, 它是不會打擾我們的程序的, 這時我們的程序可以去處理其他的邏輯, 而一旦操作系統認為數據齊全了, 他就會回調我們的提供的回調函數
- 對應操作系統來說, 當有流數據可讀時, 操作系統會將流傳入到read方法的緩沖區, 然后回調相關的 CompletionHandler
- 對於寫操作而言, 操作系統會將程序中Buffer里面數據寫入到從用戶空間寫入到系統空間 再寫入到網卡中, 寫入完畢, 同樣會回調相關的回調函數
AIO編程Server端的示例
下面貼出來一個AIO編程Server端的實例:
像下面的 read() write() accept() 全是異步的, 一經調用即刻返回, 不一樣的地方是我們會提供一個回調對象, 留給操作系統, 當操作系統認為讀寫數據都到位了, 就會去回調這些函數
public class AIOServer {
private ExecutorService executorService;
// 服務端的Channel
private AsynchronousServerSocketChannel asynchronousServerSocketChannel;
private AIOServer(int port) {
init(port);
}
// 初始化
private void init(int port) {
System.out.println("aio server start with port " + port);
executorService = Executors.newFixedThreadPool(5);
try {
// 開啟服務端的通道
asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
// 綁定端口
asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
System.out.println("server start ... ");
/**
* 方法會異步的去接收一個請求, accept()同樣是
* 參數1 : this , 暫時理解成任意類型的
* 參數2 : CompleteHandler -- 當請求到來后,會交付給 AIOServerHandler進行處理
*
* todo 在 AIO中的監聽並不是while(true), 而是類似遞歸的操作, 每次監聽到客戶端的請求后, 都需要在處理邏輯中開啟下一次的監聽
*/
asynchronousServerSocketChannel.accept(this, new AIOServerHandler());
System.out.println("------------------------------");
// 阻塞程序
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public AsynchronousServerSocketChannel getAsynchronousServerSocketChannel() {
return this.asynchronousServerSocketChannel;
}
public static void main(String[] args) {
AIOServer aioServer = new AIOServer(9999);
}
}
AIO的適用場景
AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與並發操作,編程比較復雜,JDK7開始支持。
我是bloger 賜我白日夢, 歡迎關注我 --武漢加油