通俗易懂地講解JAVA的BIO、NIO、AIO

理解Java的I/O模型(BIO、NIO、AIO)對于構建高性能網絡應用至關重要

🧠 通俗理解:快遞站的故事

想象一個快遞站:
? BIO:就像快遞站為每一個包裹都安排一位專員。專員從接到包裹到處理完(簽收、分揀、通知取件)之前,只能守著這個包裹,不能做別的事。包裹一多,專員就不夠用了。

? NIO:快遞站只安排一位大堂經理。經理會定期輪詢每個貨架(“1號貨架有新的包裹嗎?2號呢?…”)。發現某個貨架有包裹到達,就馬上安排處理。一個人可以照看很多貨架。

? AIO:快遞站裝了一套智能系統。你只需告訴系統“包裹到了就叫我”,然后就可以去忙別的。系統會在包裹真正到達時主動回調通知你:“你的包裹到了,來處理吧”。你完全不需要輪詢等待。

🔍 一、核心概念:阻塞/非阻塞 vs 同步/異步

在深入之前,先理解兩組核心概念:

  1. 阻塞與非阻塞:關注的是線程的狀態
    ? 阻塞:調用一個方法,線程會一直等待,直到該方法返回結果。

    ? 非阻塞:調用一個方法,線程立刻返回,不會傻等。你可以去做別的事。

  2. 同步與異步:關注的是消息通信的機制
    ? 同步:調用一個方法后,需要調用者自己主動等待或不斷詢問結果。

    ? 異步:調用一個方法后,調用者就去忙別的了。方法執行完畢后,會主動通知(回調)調用者。

I/O模型阻塞 vs 非阻塞同步 vs 異步通俗理解
BIO阻塞同步線程一直等,直到數據準備好
NIO非阻塞同步線程不斷輪詢,問數據好了沒
AIO非阻塞異步線程發起請求就去干別的,系統好了會回調

𝟭. BIO (Blocking I/O) - 阻塞式I/O

原理

BIO是Java最早期的I/O模型,采用“一個連接對應一個線程” 的模式。當服務器接收到一個客戶端連接時,就會創建一個新線程來處理該連接的所有讀寫操作。讀寫操作本身是阻塞的,意味著如果數據沒有準備好,線程就會一直等待,什么也干不了。

代碼示例
// 簡化版的BIO服務器代碼
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {Socket clientSocket = serverSocket.accept(); // (1) 阻塞點:等待客戶端連接new Thread(() -> { // 為每個連接創建一個新線程InputStream in = clientSocket.getInputStream();in.read(); // (2) 阻塞點:等待客戶端發送數據// ... 處理數據并響應}).start();
}

圖示理解


客戶端1 ──────> 線程1 (阻塞中...)
客戶端2 ──────> 線程2 (阻塞中...)
客戶端3 ──────> 線程3 (阻塞中...)
...

每個箭頭都代表一個獨立的線程,大部分線程都在空閑等待,浪費資源。

優缺點

? 優點:編程模型非常簡單,容易理解和上手。

? 缺點:線程是昂貴的系統資源。每個線程都需要內存(默認約1MB線程棧)和CPU調度開銷。當連接數暴增時,線程數也會線性增長,導致CPU忙于線程上下文切換,最終耗盡資源(線程爆炸),性能急劇下降。適用于連接數較少且固定的場景。

𝟮. NIO (Non-blocking I/O) - 非阻塞式I/O / 新I/O

為了解決BIO的問題,Java 1.4引入了NIO。其核心是一個線程可以處理多個連接,關鍵在于Selector(選擇器)。

核心組件

NIO有三大核心概念

  1. Channel(通道):類似于BIO中的Stream,但它是雙向的(可讀可寫),并且需要配合Buffer使用。
  2. Buffer(緩沖區):一個容器,所有數據都是通過Buffer來讀寫的。
  3. Selector(選擇器):多路復用器。一個Selector可以同時輪詢注冊在它身上的多個Channel,檢查哪些Channel已經做好了讀寫準備。這樣,單個線程就可以管理多個Channel。
工作流程(核心)
  1. 將多個Channel(比如代表連接的SocketChannel)注冊到Selector上,并告訴Selector你關心什么事件(如:連接就緒OP_ACCEPT、讀就緒OP_READ)。
  2. 線程調用Selector.select()方法阻塞,等待事件發生。
  3. 當有事件(如某個Channel可讀了)發生時,select()方法返回,并返回一個SelectionKey集合。
  4. 線程遍歷這些Key,根據事件類型(可讀、可寫等)進行相應的處理。
代碼示例
// NIO服務器代碼核心結構
Selector selector = Selector.open(); // 創建Selector
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 設置為非阻塞模式
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注冊到Selector,關注ACCEPT事件while (true) {selector.select(); // (1) 阻塞,直到有事件發生Set<SelectionKey> selectedKeys = selector.selectedKeys();for (SelectionKey key : selectedKeys) {if (key.isAcceptable()) {// 處理新連接} else if (key.isReadable()) {// 處理讀事件SocketChannel channel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer); // (2) 非阻塞讀取:即使沒數據也立即返回// ... 處理buffer中的數據}}selectedKeys.clear(); // 清理已處理的事件
}

圖示理解

            ┌─────────┐
客戶端1 ─────┤         ││         │
客戶端2 ─────┤         ├───→ 線程 (運行Selector)│Selector │
客戶端3 ─────┤         ││         │
客戶端N ─────┤         │└─────────┘

一個Selector線程可以同時監聽無數個Channel(客戶端連接)。線程只在select()處阻塞,一旦有事件到來,它就高效地去處理那些就緒的Channel

優缺點

? 優點:極大地減少了線程數量,解決了BIO的線程爆炸問題,能夠輕松管理數萬甚至更多連接。

? 缺點:編程模型復雜。需要自己管理Buffer、Channel、Selector和事件狀態機。對開發者的要求更高。本質上是同步非阻塞,因為線程仍需主動輪詢(通過Selector)就緒的Channel。

𝟯. AIO (Asynchronous I/O) - 異步I/O

Java 7引入了AIO(又稱NIO.2),它是真正的異步非阻塞I/O。

原理

AIO采用回調Future機制。用戶線程發起一個I/O操作(如read)后,立即返回,不會阻塞。應用程序可以去處理其他任務。操作系統會在底層完成整個I/O操作(將數據從內核空間拷貝到用戶空間),然后主動通知(回調)應用程序。

代碼示例 (回調式)
// AIO服務器示例 (使用回調CompletionHandler)
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));// 開始異步接受連接,并傳入一個CompletionHandler來處理接入的連接
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel client, Void attachment) {// (1) 連接建立成功的回調方法ByteBuffer buffer = ByteBuffer.allocate(1024);// (2) 開始異步讀操作,并傳入另一個CompletionHandler來處理讀完成事件client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer bytesRead, ByteBuffer buffer) {// (3) 數據讀取完成的回調方法if (bytesRead > 0) {buffer.flip();// ... 處理數據}}@Overridepublic void failed(Throwable exc, ByteBuffer buffer) {// 處理讀失敗}});// 立即再次調用accept,準備接受下一個連接server.accept(null, this);}@Overridepublic void failed(Throwable exc, Void attachment) {// 處理接受連接失敗}
});
// 主線程可以繼續做其他事,或者直接休眠
Thread.sleep(Long.MAX_VALUE);

圖示理解

用戶線程: "系統,請幫我讀數據"
操作系統: "好的"
用戶線程: [去處理其他業務邏輯...]
...
操作系統: "數據讀好了,這是結果,請你處理" (通過回調函數通知)整個過程由操作系統驅動,用戶線程只需發起請求和接收結果。
優缺點

? 優點:理論上的性能王者。線程資源利用率極高,完全不會因為I/O而阻塞。

? 缺點
1. 嚴重依賴操作系統底層的異步I/O支持(如Linux的io_uring)。在Linux上,早期的AIO實現并不完善,因此應用不如NIO廣泛。
2. 編程模型更復雜,回調嵌套可能導致“回調地獄”,代碼可讀性和維護性較差。
3. 生態和社區支持相對NIO(尤其是Netty)較弱。

📊 三者對比總結

特性BIO (阻塞I/O)NIO (非阻塞I/O)AIO (異步I/O)
核心模型一連接一線程單線程多連接(事件驅動)回調通知
阻塞與否阻塞非阻塞非阻塞
同步異步同步同步異步
線程利用率低(大量線程閑置阻塞)高(少量線程處理大量連接)極高(線程完全不阻塞)
編程復雜度低(簡單直觀)中高(需理解Selector/Buffer)高(回調地獄,異步編程)
吞吐性能低(受限于線程數)高(可應對高并發)理論最高
適用場景連接數少、快速開發高并發應用(網絡服務器、IM)極高并發、底層依賴OS

💡 實踐建議與選擇

  1. BIO:在連接數非常少且對性能要求不高的教學示例或內部工具中可能見到。如果你的應用主要是??低并發、長連接??的大文件傳輸(例如,內部系統的文件備份、少量的用戶上傳),并且希望??開發快速簡單??,那么??BIO配合分塊、斷點續傳等技術是完全可行的??。
  2. NIO:目前事實上的主流和高性能網絡編程的基石。雖然直接使用Java原生NIO API較復雜,但業界有非常成熟的Netty框架對其進行了極佳的封裝和增強。絕大多數高性能網絡應用(如Dubbo、RocketMQ、Elasticsearch)都基于Netty構建。
  3. AIO:由于平臺支持度和編程模型的原因,直接使用AIO的場景較少。在Linux系統上,Netty等框架仍然優先選擇基于NIO的模型。但在Windows(IOCP)或使用了最新io_uring的Linux系統上,AIO可能會有其用武之地。

簡單來說:現在學網絡編程,重點是理解NIO的原理,然后直接學習使用Netty框架。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96795.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96795.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96795.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LabVIEW 泵輪檢測系統

在汽車行業&#xff0c;泵輪作為液力變矩器關鍵部件&#xff0c;其質量檢測極為重要。傳統手工檢測泵輪效率低且誤差大&#xff0c;為此構建基于 LabVIEW 與西門子硬件結合的泵輪檢測系統。 應用場景 聚焦汽車零部件生產車間&#xff0c;對泵輪總成進行出廠前檢測。在液力變矩…

2025年8月月賽 T2 T3

一. 七天假日 T2原思路&#xff1a;直接計算左右括號的數量&#xff0c;然后直接輸出他們的差改進思路&#xff1a; 用d值記錄截止到當前位置&#xff0c;還需要多少個右括號可以滿足非法要求cur&#xff1a;截止到當前位置&#xff0c;已經有多少個右括號sum是右括號位置的前綴…

數據結構----棧的順序存儲(順序棧)

棧的特點&#xff1a;先進后出棧的操作&#xff1a;用數組進行存儲&#xff08;1&#xff09;初始化&#xff1a;//棧 typedef struct {int *data;//指針模擬分配數組int top;//棧“頂”指針 }Stack; //初始化 Stack InitStack(){Stack s;//給數組分配空間s.data (int*)malloc…

React Hooks原理深度解析與高級應用模式

React Hooks原理深度解析與高級應用模式 引言 React Hooks自16.8版本引入以來&#xff0c;徹底改變了我們編寫React組件的方式。然而&#xff0c;很多開發者僅僅停留在使用層面&#xff0c;對Hooks的實現原理和高級應用模式了解不深。本文將深入探討Hooks的工作原理、自定義Hoo…

兼職網|基于SpringBoot和Vue的蝸牛兼職網(源碼+數據庫+文檔)

項目介紹 : SpringbootMavenMybatis PlusVue Element UIMysql 開發的前后端分離的蝸牛兼職網&#xff0c;項目分為管理端和用戶端和企業端。 項目演示: 基于SpringBoot和Vue的蝸牛兼職網 運行環境: 最好是java jdk 1.8&#xff0c;我們在這個平臺上運行的。其他版本理論上也可…

TDengine 聚合函數 LEASTSQUARES 用戶手冊

LEASTSQUARES 函數用戶手冊 函數定義 LEASTSQUARES(expr, start_val, step_val)功能說明 LEASTSQUARES() 函數對指定列的數據進行最小二乘法線性擬合&#xff0c;返回擬合直線的斜率&#xff08;slope&#xff09;和截距&#xff08;intercept&#xff09;。該函數基于線性回…

Redis最佳實踐——安全與穩定性保障之高可用架構詳解

全面詳解 Java 中 Redis 在電商應用的高可用架構設計一、高可用架構核心模型 1. 多層級高可用體系 #mermaid-svg-anJ3iQ0ymhr025Jn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-anJ3iQ0ymhr025Jn .error-icon{fil…

ABAP 屏幕在自定義容器寫多行文本框

文章目錄變量定義容器等邏輯屏幕效果變量定義 CONSTANTS: GC_TEXT_LINE_LENGTH TYPE I VALUE 72. TYPES: TEXT_TABLE_TYPE(GC_TEXT_LINE_LENGTH) TYPE C OCCURS 0. DATA: GV_SPLITTER TYPE REF TO CL_GUI_EASY_SPLITTER_CONTAINER. DATA: GV_CUSTOM_CONTAINER TYPE REF TO CL_…

昆山精密機械公司8個Solidworks共用一臺服務器

在當今高度信息化的制造業環境中&#xff0c;昆山精密機械公司面臨著如何高效利用SolidWorks這一核心設計工具的現實挑戰。隨著企業規模的擴大和設計團隊的分散&#xff0c;傳統的單機授權模式已無法滿足協同設計需求。通過引入云飛云共享云桌面解決方案&#xff0c;該公司成功…

【WebSocket?】入門之旅(三):WebSocket 的實戰應用

本篇文章將通過構建一個簡單的實時聊天應用&#xff0c;演示如何在前端和后端搭建 WebSocket 系統&#xff0c;完成實時消息傳輸。通過實戰&#xff0c;幫助你更好地理解 WebSocket 在實際項目中的應用。 目錄 搭建 WebSocket 服務器WebSocket 客戶端實現實時聊天應用示例常見…

CentOS 8-BClinux8.2更換為阿里云鏡像源:保姆級教程

還在為 CentOS 8 官方源訪問緩慢或不可用而煩惱嗎&#xff1f;更換為國內鏡像源&#xff0c;如阿里云&#xff0c;可以顯著提升軟件包下載速度和穩定性。本文將帶你一步步完成 CentOS 8 鏡像源的更換&#xff0c;讓你的系統管理更順暢。 準備工作 在進行任何系統配置更改之前…

MySQL中InnoDB索引使用與底層原理

MySQL Server端的緩存&#xff08;查詢緩存&#xff09;是MySQL Server層的特性&#xff0c;而InnoDB的緩存&#xff08;緩沖池&#xff09;是InnoDB存儲引擎層的特性。兩者是完全獨立的。下面我們來深入探討這兩者以及InnoDB索引的原理。1. MySQL Server層的緩存 - 查詢緩存 (…

Python實戰:實現監測抖音主播是否開播并錄屏

實現這個功能,主要思路是循環檢查主播狀態 → 開播后獲取直播流地址 → 使用FFmpeg錄制。下面是一個基本的步驟表格: 步驟 關鍵行動 常用工具/庫 1 獲取主播直播間ID或唯一標識 瀏覽器開發者工具、抓包工具1 2 循環請求抖音API,查詢主播直播狀態 requests, time 3 解析API響…

init / record / required:讓 C# 對象一次成型

標簽&#xff1a; init record required with表達式 不可變性 數據模型 DTO 目錄1. init 訪問器&#xff1a;讓不可變對象的創建更靈活1.1. 概念1.1.1. 語法1.1.2. 語義1.2. 設計初衷&#xff1a;解決什么問題&#xff1f;1.3. 使用方法1.3.1. 在對象初始化器中賦值&#xff08…

每天五分鐘深度學習:神經網絡的權重參數如何初始化

本文重點 在邏輯回歸的時候,我們可以將神經網絡的權重參數初始化為0(或者同樣的值),但是如果我們將神經網絡的權重參數初始化為0就會出問題,上節課程我們已經進行了簡單的解釋,那么既然初始化為0不行,神經網絡該如何進行參數初始化呢?神經網絡的權重參數初始化是模型訓…

[論文閱讀] 告別“數量為王”:雙軌道會議模型+LS,破解AI時代學術交流困局

告別“數量為王”&#xff1a;雙軌道會議模型LS&#xff0c;破解AI時代學術交流困局 論文信息信息類別具體內容論文原標題From Passive to Participatory: How Liberating Structures Can Revolutionize Our Conferences主要作者及機構1. Daniel Russo&#xff08;丹麥奧爾堡大…

趣味學solana(介紹)

你就是那個關鍵的“守門員”&#xff01; 為了方便理解Solana&#xff0c;我們把Solana 想象成一個巨大的、24小時不停歇的足球聯賽。成千上萬的足球運動員&#xff08;用戶&#xff09;在不停地傳球、射門&#xff08;發送交易&#xff09;&#xff0c;而整個比賽的結果必須被…

分布式事務性能優化:從故障現場到方案落地的實戰手記(三)

第三部分&#xff1a;混合場景攻堅——從“單點優化”到“系統協同” 有些性能問題并非單一原因導致&#xff0c;而是鎖競爭與事務耗時共同作用的結果。以下2個案例&#xff0c;展示綜合性優化策略。 案例7&#xff1a;基金申購的“TCC性能陷阱”——從全量預留到增量確認 故障…

規則系統架構風格

考題 某公司擬開發一個VIP管理系統,系統需要根據不同商場活動,不定期更新VIP會員的審核標準和VIP折扣系統。針對上述需求,采用(__)架構風格最為合適。 A. 規則系統 B. 管道-過濾器風格 C. 事件驅動 D. 分層 一、什么是規則系統架構風格? 規則系統架構風格是一種將應…

kubeadm搭建生產環境的單master多node的k8s集群

k8s環境規劃: podSubnet&#xff08;pod 網段&#xff09; 10.20.0.0/16 serviceSubnet&#xff08;service 網段&#xff09;: 10.10.0.0/16 實驗環境規劃: 操作系統&#xff1a;centos7.9 配置&#xff1a; 4G 內存/4核CPU/40G 硬盤 網絡&#xff1a;NAT K8s集群角色ip主…