程式設計師必知面試技術,程式設計面試IO模型有幾種?分別是什麼?

weixin_33890499發表於2018-10-11

《Java網路程式設計面試題》

出版單位:北京尚學堂優效學院

優效學院由清華大學著名的IT教育領導者馬士兵老師創辦,是一家線上線下相互融合的網際網路+培訓機構。公司均由海外留學生和國內行業精英人士擔任授課講師,主要成員均碩士且擁有十多年的行業經驗。畢業學生就職於國內BAT以及海外著名公司。優效學院,名師執教,高效學習,成就未來。

著:張洋

11年工作經驗 曾就職聯眾遊戲(程式設計師)、眾信旅遊(Team Leader)、精智教育(聯合創始人)、中國石化(大資料高階顧問) 精通javaEE體系、網際網路產品架構,熟悉Sap Bw/HANA、多個大資料專案經驗

20180926版

IO模型有幾種?分別是什麼?

在《Unix網路程式設計》一書中提到了五種IO模型

分別是:阻塞IO、非阻塞IO、多路複用IO、訊號驅動IO以及非同步IO。

下面就分別來介紹一下這5種IO模型的異同。

1.阻塞IO模型

最傳統的一種IO模型,即在讀寫資料過程中會發生阻塞現象。

當使用者執行緒發出IO請求之後,核心會去檢視資料是否就緒,如果沒有就緒就會等待資料就緒,而使用者執行緒就會處於阻塞狀態,使用者執行緒交出CPU。當資料就緒之後,核心會將資料拷貝到使用者執行緒,並返回結果給使用者執行緒,使用者執行緒才解除block狀態。

典型的阻塞IO模型的例子為:

data = socket.openinputstream();

如果資料沒有就緒,就會一直阻塞在read方法。

6758197-6bcdef4f358c3d59
阻塞IO模型

2.非阻塞IO模型

當使用者執行緒發起一個read操作後,並不需要等待,而是馬上就得到了一個結果。如果結果是一個error時,它就知道資料還沒有準備好,於是它可以再次傳送read操作。一旦核心中的資料準備好了,並且又再次收到了使用者執行緒的請求,那麼它馬上就將資料拷貝到了使用者執行緒,然後返回。

所以事實上,在非阻塞IO模型中,使用者執行緒需要不斷地詢問核心資料是否就緒,也就說非阻塞IO不會交出CPU,而會一直佔用CPU。

典型的非阻塞IO模型一般如下:

虛擬碼

while(true){

new MyThread(socket)

}

class MyThread{

data = socket.read();

if(data!= error){

處理資料

break;

}

但是對於非阻塞IO就有一個非常嚴重的問題,在while迴圈中需要不斷地去詢問核心資料是否就緒,這樣會導致CPU佔用率非常高,因此一般情況下很少使用while迴圈這種方式來讀取資料。

6758197-ca7f75dc91ab4f3c
非阻塞IO模型

3.多路複用IO模型

多路複用IO模型是目前使用得比較多的模型。Java NIO實際上就是多路複用IO。

在多路複用IO模型中,會有一個執行緒不斷去輪詢多個socket的狀態,只有當socket真正有讀寫事件時,才真正呼叫實際的IO讀寫操作。因為在多路複用IO模型中,只需要使用一個執行緒就可以管理多個socket,系統不需要建立新的程式或者執行緒,也不必維護這些執行緒和程式,並且只有在真正有socket讀寫事件進行時,才會使用IO資源,所以它大大減少了資源佔用。

在Java NIO中,是通過selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那裡,因此這種方式會導致使用者執行緒的阻塞。

也許有朋友會說,我可以採用 多執行緒+ 阻塞IO 達到類似的效果,但是由於在多執行緒 + 阻塞IO 中,每個socket對應一個執行緒,這樣會造成很大的資源佔用,並且尤其是對於長連線來說,執行緒的資源一直不會釋放,如果後面陸續有很多連線的話,就會造成效能上的瓶頸。

而多路複用IO模式,通過一個執行緒就可以管理多個socket,只有當socket真正有讀寫事件發生才會佔用資源來進行實際的讀寫操作。因此,多路複用IO比較適合連線數比較多的情況。

另外多路複用IO為何比非阻塞IO模型的效率高是因為在非阻塞IO中,不斷地詢問socket狀態時通過使用者執行緒去進行的,而在多路複用IO中,輪詢每個socket狀態是核心在進行的,這個效率要比使用者執行緒要高的多。

不過要注意的是,多路複用IO模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件逐一進行響應。因此對於多路複用IO模型來說,一旦事件響應體很大,那麼就會導致後續的事件遲遲得不到處理,並且會影響新的事件輪詢。

6758197-b7a879eaed30763b
多路複用IO模型

4.訊號驅動IO模型

在訊號驅動IO模型中,當使用者執行緒發起一個IO請求操作,會給對應的socket註冊一個訊號函式,然後使用者執行緒會繼續執行,當核心資料就緒時會傳送一個訊號給使用者執行緒,使用者執行緒接收到訊號之後,便在訊號函式中呼叫IO讀寫操作來進行實際的IO請求操作。

6758197-3dcf5dda11e81a72
訊號驅動IO模型

5.非同步IO模型

非同步IO模型才是最理想的IO模型,在非同步IO模型中,當使用者執行緒發起read操作之後,立刻就可以開始去做其它的事。

而另一方面,從核心的角度,當它受到一個asynchronous read之後,它會立刻返回,說明read請求已經成功發起了,因此不會對使用者執行緒產生任何block。

然後,核心會等待資料準備完成,然後將資料拷貝到使用者執行緒,當這一切都完成之後,核心會給使用者執行緒傳送一個訊號,告訴它read操作完成了。

也就說使用者執行緒完全不需要實際的整個IO操作是如何進行的,只需要先發起一個請求,當接收核心返回的成功訊號時表示IO操作已經完成,可以直接去使用資料了。

也就說在非同步IO模型中,IO操作的兩個階段都不會阻塞使用者執行緒,這兩個階段都是由核心自動完成,然後傳送一個訊號告知使用者執行緒操作已完成。

使用者執行緒中不需要再次呼叫IO函式進行具體的讀寫。

這點是和訊號驅動模型有所不同的

在訊號驅動模型中,當使用者執行緒接收到訊號表示資料已經就緒,然後需要使用者執行緒呼叫IO函式進行實際的讀寫操作;而在非同步IO模型中,收到訊號表示IO操作已經完成,不需要再在使用者執行緒中呼叫iO函式進行實際的讀寫操作。

注意,非同步IO是需要作業系統的底層支援,在Java 7中,提供了Asynchronous IO。也就是java中的AIO NIO2.0

6758197-ee8f86aa29b276db
非同步IO模型

前面四種IO模型實際上都屬於同步IO,只有最後一種是真正的非同步IO,因為無論是多路複用IO還是訊號驅動模型,IO操作的第2個階段都會引起使用者執行緒阻塞,也就是核心進行資料拷貝的過程都會讓使用者執行緒阻塞。

本文章為連載內容,大家可以持續關注小編,我將盡其所能的為大家提供技術性實踐資料、文章、視訊。

感謝大家的支援!

相關文章