天天看點

Java IO多路複用機制詳解

1、在Java中,常見的IO模型有4種,

  • 同步阻塞IO(Blocking IO)
  • 同步非阻塞IO(Non-blocking IO):預設建立的socket都是阻塞的,非阻塞IO要求socket被設定為NONBLOCK。注意這裡所說的NIO并非Java的NIO(New IO)庫。
  • IO多路複用(IO Multiplexing):也稱為異步阻塞IO,Java中的Selector和Linux中的epoll都是這種模型。這裡複用的是指複用一個或幾個線程,用一個或一組線程處理多個IO操作,減少系統開銷小,不必建立和維護過多的程序/線程;
  • 異步IO(Asynchronous IO):即經典的Proactor設計模式,也稱為異步非阻塞IO。

2、同步與阻塞概念

 1)同步和異步的概念描述的是使用者線程與核心的互動方式

  • 同步是指使用者線程發起IO請求後需要等待或者輪詢核心IO操作完成後才能繼續執行;
  • 異步是指使用者線程發起IO請求後仍繼續執行,當核心IO操作完成後會通知使用者線程,或者調用使用者線程注冊的回調函數。

 2)阻塞和非阻塞的概念描述的是使用者線程調用核心IO操作的方式

  • 阻塞是指IO操作需要徹底完成後才傳回到使用者空間;
  • 非阻塞是指IO操作被調用後立即傳回給使用者一個狀态值,無需等到IO操作徹底完成。

“真正”的異步IO需要作業系統更強的支援。在IO多路複用模型中,事件循環将檔案句柄的狀态事件通知給使用者線程,由使用者線程自行讀取資料、處理資料。而在異步IO模型中,當使用者線程收到通知時,資料已經被核心讀取完畢,并放在了使用者線程指定的緩沖區内,核心在IO完成後通知使用者線程直接使用即可。(圖檔來自https://blog.csdn.net/sehanlingfeng/article/details/78920423)

Java IO多路複用機制詳解

相比于IO多路複用模型,異步IO并不十分常用,不少高性能并發服務程式使用IO多路複用模型+多線程任務處理的架構基本可以滿足需求。Java的NIO是基于I/O複用來實作的。

3、IO多路複用的底層原理

IO多路複用使用兩個系統調用(select/poll/epoll和recvfrom),blocking IO隻調用了recvfrom;select/poll/epoll 核心是可以同時處理多個connection,而不是更快,是以連接配接數不高的話,性能不一定比多線程+阻塞IO好,多路複用模型中,每一個socket,設定為non-blocking,阻塞是被select這個函數block,而不是被socket阻塞的。

1)select機制

用戶端操作伺服器時就會産生這三種檔案描述符(簡稱fd):writefds(寫)、readfds(讀)、和exceptfds(異常)。select會阻塞住監視3類檔案描述符,等有資料、可讀、可寫、出異常 或逾時、就會傳回;傳回後通過周遊fdset整個數組來找到就緒的描述符fd,然後進行對應的IO操作。

優點:

  幾乎在所有的平台上支援,跨平台支援性好

缺點:

  由于是采用輪詢方式全盤掃描,會随着檔案描述符FD數量增多而性能下降。

  每次調用 select(),需要把 fd 集合從使用者态拷貝到核心态,并進行周遊(消息傳遞都是從核心到使用者空間)

  預設單個程序打開的FD有限制是1024個,可修改宏定義,但是效率仍然慢。

2)poll機制

基本原理與select一緻,隻是沒有最大檔案描述符限制,因為采用的是連結清單存儲fd。

3)epoll機制

epoll之是以高性能是得益于它的三個函數

  1)epoll_create()系統啟動時,在Linux核心裡面申請一個B+樹結構檔案系統,傳回epoll對象,也是一個fd

  2)epoll_ctl() 每建立一個連接配接,都通過該函數操作epoll對象,在這個對象裡面修改添加删除對應的連結fd, 綁定一個callback函數。

  3)epoll_wait() 輪訓所有的callback集合,并完成對應的IO操作

優點:

  沒fd這個限制,所支援的FD上限是作業系統的最大檔案句柄數,1G記憶體大概支援10萬個句柄

  效率提高,使用回調通知而不是輪詢的方式,不會随着FD數目的增加效率下降

  核心和使用者空間mmap同一塊記憶體實作