什麼是同步與異步?阻塞與非阻塞有何區別?本文從 Unix I/O 模型切入,介紹五種常見 I/O 模型,進而闡述 Java I/O 的演進歷程,並透過實例說明如何選擇適合的模型以提升系統併發量與可用性。由於 Java I/O 依賴作業系統實作,理解 Unix I/O 模型有助於深入掌握 Java I/O。
描述使用者執行緒與核心的交互方式:
描述使用者執行緒呼叫核心 I/O 操作的方式:
一個 I/O 操作分兩階段:發起請求與實際 I/O 操作。阻塞/非阻塞區別在第一階段;同步/異步區別在第二階段是否阻塞請求進程。
請求無法立即完成則保持阻塞。兩階段:等待資料就緒、資料從核心拷貝到使用者空間。

Socket 設為 NONBLOCK,請求無法完成時返回錯誤碼,使用者執行緒需不斷輪詢直到資料就緒,消耗大量 CPU。較少直接使用,多用於 I/O 多路複用基礎。

使用 select/poll 同時監控多個 I/O 操作,單一執行緒可處理多個 Socket。採用 Reactor 設計模式,由專職執行緒輪詢模擬第一階段異步化。

允許 Socket 進行信號驅動,資料就緒時進程收到 SIGIO 信號,在處理函數中執行 I/O 操作。

呼叫 aio_read 後立即返回,核心將資料拷貝到緩衝區後通知應用程式。採用 Proactor 設計模式,兩階段均異步。

前四種模型第二階段相同(核心拷貝到使用者緩衝區),異步 I/O 兩階段均不同。同步 I/O 會阻塞請求進程,異步 I/O 不會。

傳統 ServerSocket.accept() 與 InputStream.read() 均阻塞,單執行緒僅能處理一個連線。
主執行緒接受連線,為每個連線建立新執行緒處理。問題:頻繁建立/銷毀執行緒開銷大,大量短連接時效能低。
使用執行緒池重用執行緒,減少建立銷毀開銷。大量長連接時執行緒長期被佔用,優勢不明顯。
利用 Selector、Channel、Buffer 核心組件,單一執行緒管理成千上萬連線,讀寫事件就緒時再派發給執行緒池處理業務,極大提升併發能力。
Java 7 引入 AsynchronousServerSocketChannel,提供真正的異步操作,結合執行緒池構建高效能網路應用。
原始碼範例可參考:https://github.com/waylau/essential-java 中 com.waylau.essentialjava.net.echo 套件。