1.關於NIO、BIO
BIO blocking IO
NIO non-blocking IO (因為是JDK1.4之后推出的也有稱為 new IO,感覺前者更貼切吧)
經過一段時間的學習才發現,原來NIO、BIO並不是由上層的處理模式決定的,只是一般來說,BIO的通信處理模式底層的IO是阻塞的(blocking),而NIO的通信處理模式是的底層IO是非阻塞的(NIO non-blocking),底層的IO的特性當然也會影響和決定上層的設計,所以之前習慣性的就認為NIO就是說的NIO的通信處理模式(可能多數剛了解的都會這樣理解吧),說什么NIO就是用一個線程來處理多個請求,不再像BIO那樣,針對每一個請求都單獨創建一個線程,所以NIO效率比BIO高等等。當時的理解確實太過淺顯,現在把自己當前的理解記錄一下,當然也參考了不少網上的資料。
2.BIO和NIO的區別:
BIO: 阻塞IO,底層使用的是阻塞IO模型,面向流,流意味着單向性,而且BIO面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前后移動流中的數據。通過JDK的源碼InputStream中的read方法的注釋可以看到,讀取數據的時候會發生阻塞 (見下方代碼)直到:1.有數據可讀2.數據讀取完畢3.發生異常,同理可見OutputStream的write()方法的注釋也是一樣的,會一直阻塞直到:1所有字節發送完畢2.發生異常。這意味着當網絡傳輸比較緩慢的時候,讀取輸入流的乙方的通信線程將被長時間阻塞,如果輸入流的一方需要一分鍾才能把1K的數據發送完,那么讀取的一方的IO線程也會被阻塞一分鍾。同理,如果讀取一方的網絡緩慢不能及時從TCP的緩沖區中讀取數據,這將會導致發送方的的Window Size不斷減小,直到為0,雙方處於keep-Allive狀態,消息發送方將不能再向TCP緩沖區寫入數據,直到TCP緩沖區的Windw Size大於0或者發生IO異常。由於Socket IO會阻塞,可以為每一個Socket創建一個Thread,實現多並發,但是這樣的系統開銷和資源浪費都太大,不是合理選擇,可以使用線程池進行緩解,但大量連接的情況下,也應該會比較慢。
read方法的注解
NIO:非阻塞,底層使用的是IO復用模型(同步非阻塞),面向緩沖區,Java NIO的緩沖導向方法略有不同,數據讀取到一個它稍后處理的緩沖區,需要時可在緩沖區中前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩沖區時,不要覆蓋緩沖區里尚未處理的數據。
NIO的主要三個核心模塊:Selector(選擇器),Channel(通道),Buffer(緩沖區)。Selector是選擇器,Channel(Socket通信的通道)需要注冊到選擇器上,並且多個通道可以注冊到一個選擇器上面,這樣可以使用一個線程就能管理多個通道,避免了BIO的問題,效率高出很多,而且減少了系統的開銷。Selector可以輪詢調用select(),這個方法會阻塞(但是也可以指定阻塞時間,到達時間后會立即返回)。當注冊的某個通道准備好要進行IO操作時,這個便返回已選擇鍵的個數,此時通過selectedKeys獲得已選擇的鍵(SelectionKey),通過SelectionKey可以關聯到對應的Channel,然后就可以進行相關的IO操作了,另外,NIO的數據讀和寫使用的都是 Buffer,Channel從Buffer中讀取數據或者將數據寫入到Buffer中。