java網路程式設計(一)----概論
近來看了bio,nio,aio覺得有些混雜,想寫幾篇部落格來梳理總結一下。
我看了那麼久才總結出來,你們現在可以直接看總結馬上快速學習,真羨慕。
本章將會分為四個部分:
- 概論
- 同步阻塞bio
- 同步非阻塞nio及reactor模型
- 非同步非阻塞aio及proactor模型
是否阻塞和同步非同步都是程式實現方式,這裡只講述這些實現方式在網路IO方面的作用及影響。
是否阻塞&同異步
在學習IO體系的過程中接觸到最多的四個名詞
- 阻塞
- 非阻塞
- 同步
- 非同步
名詞 | 解釋 | 舉例 |
---|---|---|
阻塞 | 當試圖對IO流進行讀寫時, 如果當時沒有東西可讀或者暫時不可寫, 程式就進入等待狀態, 直到有東西可讀或者可寫才繼續執行 | 電話接通,你豎著耳朵聽,等待對方說話,對方不說你就一直等著,直到對方說了話你就開始聽取他說的話。等待說話的過程你沒辦法做其他事就一直等著 |
非阻塞 | 非阻塞狀態下, 如果沒有東西可讀, 或者不可寫, 讀寫函式馬上返回, 而不會等待 | 小時候等媽媽買菜回家,想在樓下幫媽媽拿東西上樓,你下樓看看你媽媽到了沒,要是沒到你們上樓繼續看你的電視,想著過一會兒再來下看看,而不是在樓下等待 |
同步 | 使用者程序觸發IO操作並等待或者輪詢的去檢視IO操作是否就緒 | 自己上街買衣服,自己親自幹這件事,別的事幹不了。 |
非同步 | 非同步是指使用者程序觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知(非同步的特點就是通知) | 告訴朋友自己合適衣服的尺寸,大小,顏色,讓朋友委託去買,然後自己可以去幹別的事,等朋友回來就會跟我們說買的結果(使用非同步IO時,Java將IO讀寫委託給OS處理,需要將資料緩衝區地址和大小傳給OS) |
一、阻塞和非阻塞
是針對於程序在訪問資料的時候,根據IO操作的就緒狀態來採取的不同方式,說白了是一種讀取或者寫入操作函式的實現方式,阻塞方式下讀取或者寫入函式將一直等待,而非阻塞方式下,讀取或者寫入函式會立即返回一個狀態值。
二、 同步和非同步
1. 是針對應用程式和主執行緒的互動而言的;
2. 如果主執行緒需要等待執行過程,直到結束才能繼續執行下面的程式碼,那麼無疑就是同步;
3. 而非同步多與回撥機制一起使用,為什麼呢?在主執行緒中我們使用非同步呼叫之後就什麼都不理繼續執行下面的程式。那麼非同步呼叫的程式執行的情況結果我們不就不得而知,這個時候就使用回撥呼叫主執行緒的“通知程式”讓主執行緒獲知是否成功改做什麼處理的等等;
4. 非同步實現有兩種定義,第一是某個工作交給另外一個執行緒去執行,自己不需要涉及處理過程,直到非同步執行緒處理結束之後回撥。二是在非同步IO通訊中,IO操作是呼叫了系統的api幫你主動處理連線和讀寫資料,然後回撥直接傳輸迴應用層,不用自己呼叫函式去獲取,執行完成後返回結果。
不熟悉回撥的可以看一下:
下面我們再來理解組合方式的IO型別,就好理解多了。
同步阻塞IO(java BIO):
最入門的網路程式設計中,我們在伺服器端使用ServerSocket的accept()方法等待連線,或者使用read()和write()方法進行資料讀寫,當沒有請求連線或者沒有資料進行讀寫時,程式會“卡在”那行程式碼處,直到有請求或者資料程式碼才繼續執行。這就是阻塞,而同步為讀寫過程中程式都是在所在主執行緒執行,我們需要等待執行結束之後才能進行其他的程式碼操作。同步非阻塞IO(java NIO):
java的nio即為同步非阻塞IO,客戶端傳送的連線和讀寫請求都會註冊到多路複用器上,注意,這裡就不會在某一個連線或者讀寫請求上阻塞住,註冊完之後我們可做自己的事,需要的話再通過多路複用器select()到連線請求或I/O請求時才進行處理,而這個select()操作佔用著我們的主執行緒,所以稱為同步。非同步阻塞IO:
非同步就是不阻塞的,所以這個非同步阻塞概念有點矛盾。阻塞與非阻塞都可以理解為同步範疇下才有的概念,對於非同步,我們直接發起一個IO操作呼叫了作業系統api,接著就可以執行我們接下來的程式,非同步IO操作結束後就會通知主執行緒。所以非同步下對於我們的主執行緒來說是肯定不會阻塞的。非同步非阻塞IO(Java AIO(NIO.2)):
在此種模式下,使用者程序只需要發起一個IO操作然後什麼都不用理繼續執行我們接下來的程式,等IO操作真正的完成以後,應用程式會通過回撥呼叫我們實現的介面進行處理,而真正的IO操作已經委託JVM或者作業系統完成了。
同步和非同步是針對應用程式和核心的互動而言的,同步指的是使用者程序觸發IO操作並等待執行過程,而非同步是指使用者程序觸發IO操作以後便開始做自己的事情,而當IO操作已經完成的時候會得到IO完成的通知。而阻塞和非阻塞是針對於程序在訪問資料的時候,根據IO操作的就緒狀態來採取的不同方式,說白了是一種讀取或者寫入操作函式的實現方式,阻塞方式下如果資料未準備完畢,讀取或者寫入函式將一直等待,而非阻塞方式下,讀取或者寫入函式會立即返回一個狀態值。
我覺得我要講通俗一點,你要“在冰箱拿一個雪糕喂貓”:
- 你在冰箱找雪糕,找不到就一直等直到冰箱有雪糕,你就拿了喂貓。這個事情是你在做,你找不到冰淇淋你要等知道有,這是同步阻塞。
- 你在冰箱找雪糕,找不到去做其他事,等下再來找,找到了就拿了喂貓。這個事情是你在做,找不到雪糕就走等下再來,這是同步非阻塞。
- 你跟你兒子(作業系統)說,看到那個冰箱沒,看到那隻貓沒,去冰箱拿雪糕喂貓。然後你就去做其他事了,你兒子做好之後再來跟你說做得是否成功。那麼兒子究竟是怎麼做的,是一直等著冰箱有雪糕再去拿還是隔一會兒看一次,還是直接叫毛自己吃,這個跟我們沒關係(底層實現),讓傻逼兒子去做就好。我們跟兒子說一聲就去做自己的事,到時兒子會自己來告訴我們,很明顯沒有我們不會阻塞,事情也不是我們做的,故為非同步非阻塞。
- 而為什麼沒有非同步阻塞呢?這個是對應去冰箱拿雪糕直到有雪糕才結束,然後接著叫兒子去喂貓。
我們如果把“在冰箱拿雪糕喂貓”作為一件不可拆分的事,那麼很明顯,非同步是肯定不會阻塞的,因為我們直接叫兒子去冰箱拿雪糕喂貓就去做自己的事了,他是不是阻塞地去拿雪糕不關我們事,我們命令一下就走了。
那如果把拿冰淇淋和喂貓分出來,就像上面說的我們去冰箱拿雪糕直到有雪糕才走,然後把雪糕給兒子叫他去喂貓。這個確實看起來跟非同步阻塞一樣,可是java並沒有實現啊哈哈哈。而且也根本沒必要實現,非同步非阻塞aio已經作為一個整體動作幫我們完成了這個操作。就算你自己寫了多執行緒,自己寫一個兒子執行緒去非同步執行喂貓操作,那也不叫做非同步。前面說了非同步有兩種,這裡的是IO非同步,我們自己程式即使你用了多執行緒處理進行喂貓操作也是“偽非同步”,網路IO非同步指的是你直接叫作業系統幫你做拿雪糕和喂貓這件事。
BIO、NIO、AIO適用場景分析:
BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程式直觀簡單易理解。
NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜,JDK1.4開始支援。
AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。