BIO(Blocking I/O)同步阻塞I/Ojava
這是最基本與簡單的I/O操做方式,其根本特性是作完一件事再去作另外一件事,一件事必定要等前一件事作完,這很符合程序員傳統的順序來開發思想,所以BIO模型程序開發起來較爲簡單,易於把握。程序員
可是BIO若是須要同時作不少事情(例如同時讀不少文件,處理不少tcp請求等),就須要系統建立不少線程來完成對應的工做,由於BIO模型下一個線程同時只能作一個工做,若是線程在執行過程當中依賴於須要等待的資源,那麼該線程會長期處於阻塞狀態,咱們知道在整個操做系統中,線程是系統執行的基本單位,在BIO模型下的線程 阻塞就會致使系統線程的切換,從而對整個系統性能形成必定的影響。固然若是咱們只須要建立少許可控的線程,那麼採用BIO模型也是很好的選擇,但若是在須要考慮高併發的web或者tcp服務器中採用BIO模型就沒法應對了,若是系統開闢成千上萬的線程,那麼CPU的執行時機都會浪費在線程的切換中,使得線程的執行效率大大下降。此外,關於線程這裏說一句題外話,在系統開發中線程的生命週期必定要準確控制,在須要必定規模併發的情形下,儘可能使用線程池來確保線程建立數目在一個合理的範圍以內,切莫編寫線程數量建立上限的代碼。web
NIO (New I/O) 同步非阻塞I/O編程
關於NIO,國內有不少技術博客將英文翻譯成No-Blocking I/O,非阻塞I/O模型 ,固然這樣就與BIO造成了鮮明的特性對比。NIO自己是基於事件驅動的思想來實現的,其目的就是解決BIO的大併發問題,在BIO模型中,若是須要併發處理多個I/O請求,那就須要多線程來支持,NIO使用了多路複用器機制,以socket使用來講,多路複用器經過不斷輪詢各個鏈接的狀態,只有在socket有流可讀或者可寫時,應用程序才須要去處理它,在線程的使用上,就不須要一個鏈接就必須使用一個處理線程了,而是隻是有效請求時(確實須要進行I/O處理時),纔會使用一個線程去處理,這樣就避免了BIO模型下大量線程處於阻塞等待狀態的情景。windows
相對於BIO的流,NIO抽象出了新的通道(Channel)做爲輸入輸出的通道,而且提供了緩存(Buffer)的支持,在進行讀操做時,須要使用Buffer分配空間,而後將數據從Channel中讀入Buffer中,對於Channel的寫操做,也須要現將數據寫入Buffer,而後將Buffer寫入Channel中。數組
以下是NIO方式進行文件拷貝操做的示例,見下圖:緩存
經過比較New IO的使用方式咱們能夠發現,新的IO操做再也不面向 Stream來進行操做了,改成了通道Channel,而且使用了更加靈活的緩存區類Buffer,Buffer只是緩存區定義接口, 根據須要,咱們能夠選擇對應類型的緩存區實現類。在java NIO編程中,咱們須要理解如下3個對象Channel、Buffer和Selector。服務器
Channel多線程
首先說一下Channel,國內大多翻譯成「通道」。Channel和IO中的Stream(流)是差很少一個等級的。只不過Stream是單向的,譬如:InputStream, OutputStream。而Channel是雙向的,既能夠用來進行讀操做,又能夠用來進行寫操做,NIO中的Channel的主要實現有:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel;經過看名字就能夠猜出個因此然來:分別能夠對應文件IO、UDP和TCP(Server和Client)。併發
Buffer
NIO中的關鍵Buffer實現有:ByteBuffer、CharBuffer、DoubleBuffer、 FloatBuffer、IntBuffer、 LongBuffer,、ShortBuffer,分別對應基本數據類型: byte、char、double、 float、int、 long、 short。固然NIO中還有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等這裏先不具體陳述其用法細節。
說一下 DirectByteBuffer 與 HeapByteBuffer 的區別?
它們 ByteBuffer 分配內存的兩種方式。HeapByteBuffer 顧名思義其內存空間在 JVM 的 heap(堆)上分配,能夠看作是 jdk 對於 byte[] 數組的封裝;而 DirectByteBuffer 則直接利用了系統接口進行內存申請,其內存分配在c heap 中,這樣就減小了內存之間的拷貝操做,如此一來,在使用 DirectByteBuffer 時,系統就能夠直接從內存將數據寫入到 Channel 中,而無需進行 Java 堆的內存申請,複製等操做,提升了性能。既然如此,爲何不直接使用 DirectByteBuffer,還要來個 HeapByteBuffer?緣由在於, DirectByteBuffer 是經過full gc來回收內存的,DirectByteBuffer會本身檢測狀況而調用 system.gc(),可是若是參數中使用了 DisableExplicitGC 那麼就沒法回收該快內存了,-XX:+DisableExplicitGC標誌自動將 System.gc() 調用轉換成一個空操做,就是應用中調用 System.gc() 會變成一個空操做,那麼若是設置了就須要咱們手動來回收內存了,因此DirectByteBuffer使用起來相對於徹底託管於 java 內存管理的Heap ByteBuffer 來講更復雜一些,若是用很差可能會引發OOM。Direct ByteBuffer 的內存大小受 -XX:MaxDirectMemorySize JVM 參數控制(默認大小64M),在 DirectByteBuffer 申請內存空間達到該設置大小後,會觸發 Full GC。
Selector
Selector 是NIO相對於BIO實現多路複用的基礎,Selector 運行單線程處理多個 Channel,若是你的應用打開了多個通道,但每一個鏈接的流量都很低,使用 Selector 就會很方便。例如在一個聊天服務器中。要使用 Selector , 得向 Selector 註冊 Channel,而後調用它的 select() 方法。這個方法會一直阻塞到某個註冊的通道有事件就緒。一旦這個方法返回,線程就能夠處理這些事件,事件的例子有如新的鏈接進來、數據接收等。
這裏咱們再來看一個NIO模型下的TCP服務器的實現,咱們能夠看到Selector 正是NIO模型下 TCP Server 實現IO複用的關鍵,請仔細理解下段代碼while循環中的邏輯,見下圖:
AIO (Asynchronous I/O) 異步非阻塞I/O
Java AIO就是Java做爲對異步IO提供支持的NIO.2 ,Java NIO2 (JSR 203)定義了更多的 New I/O APIs, 提案2003提出,直到2011年才發佈, 最終在JDK 7中才實現。JSR 203除了提供更多的文件系統操做API(包括可插拔的自定義的文件系統), 還提供了對socket和文件的異步 I/O操做。 同時實現了JSR-51提案中的socket channel所有功能,包括對綁定, option配置的支持以及多播multicast的實現。
從編程模式上來看AIO相對於NIO的區別在於,NIO須要使用者線程不停的輪詢IO對象,來肯定是否有數據準備好能夠讀了,而AIO則是在數據準備好以後,纔會通知數據使用者,這樣使用者就不須要不停地輪詢了。固然AIO的異步特性並非Java實現的僞異步,而是使用了系統底層API的支持,在Unix系統下,採用了epoll IO模型,而windows即是使用了IOCP模型。關於Java AIO,本篇只作一個拋磚引玉的介紹,若是你在實際工做中用到了,那麼能夠參考Netty在高併發下使用AIO的相關技術。
總 結
IO實質上與線程沒有太多的關係,可是不一樣的IO模型改變了應用程序使用線程的方式,NIO與BIO的出現解決了不少BIO沒法解決的併發問題,固然任何技術拋開適用場景都是耍流氓,複雜的技術每每是爲了解決簡單技術沒法解決的問題而設計的,在系統開發中能用常規技術解決的問題,毫不用複雜技術,不然大大增長系統代碼的維護難度,學習IT技術不是爲了炫技,而是要實實在在解決問題。