java之bio與nio圖解(Javanio)

本文目錄一覽:

java中bio nio aio的區別和聯繫

BIO是一個連接一個線程。

NIO是一個請求一個線程。

AIO是一個有效請求一個線程。

先來個例子理解一下概念,以銀行取款為例:

同步 : 自己親自出馬持銀行卡到銀行取錢(使用同步IO時,Java自己處理IO讀寫);

異步 : 委託一小弟拿銀行卡到銀行取錢,然後給你(使用異步IO時,Java將IO讀寫委託給OS處理,需要將數據緩衝區地址和大小傳給OS(銀行卡和密碼),OS需要支持異步IO操作API);

阻塞 : ATM排隊取款,你只能等待(使用阻塞IO時,Java調用會一直阻塞到讀寫完成才返回);

非阻塞 : 櫃檯取款,取個號,然後坐在椅子上做其它事,等號廣播會通知你辦理,沒到號你就不能去,你可以不斷問大堂經理排到了沒有,大堂經理如果說還沒到你就不能去(使用非阻塞IO時,如果不能讀寫Java調用會馬上返回,當IO事件分發器會通知可讀寫時再繼續進行讀寫,不斷循環直到讀寫完成)

Java對BIO、NIO、AIO的支持:

Java BIO : 同步並阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。

Java NIO : 同步非阻塞,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會註冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。

Java AIO(NIO.2) : 異步非阻塞,服務器實現模式為一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啟動線程進行處理,

java與mysql是nio還是bio

Java中的IO方式主要分為3種:BIO(同步阻塞)、NIO(同步非阻塞)和AIO(異步非阻塞)。

BIO

同步阻塞模式。在JDK1.4以前,使用Java建立網絡連接時,只能採用BIO方式,在服務器端啟動一個ServerSocket,然後使用accept等待客戶端請求,對於每一個請求,使用一個線程來進行處理用戶請求。線程的大部分時間都在等待請求的到來和IO操作,利用率很低。而且線程的開銷比較大,數量有限,因此服務器同時能處理的連接數也很低。

NIO

BIO模式中,是“一個Socket一個線程”;而在NIO中則是使用單個或少量的線程來輪詢Socket,當發現Socket上有請求時,才為請求分配線程。因此是“一個請求一個線程”。

具體實現就是把Socket通過Channel註冊到Selector,使用一個線程在Selector中輪詢,發現Channel有讀寫的事件,就可以分配給其他線程來處理(通常使用線程池)。

AIO

從JDK7開始支持AIO模式。通過AsynchronousServerSocketChannel中註冊事件回調函數來處理業務邏輯。當IO操作完成以後,回調函數會被調用。如果傳入AsynchronousChannelGroup,可以綁定線程池來處理事件。

關於JDK的實現,Windows平台基於IOCP實現AIO,Linux只有eppoll模擬實現了AIO。

附:關於IO經常提到Reactor/ Proactor模式。

這兩個模式中都有兩個角色:事件多路分離器(Event Demultiplexer)和事件處理器(Event Handler)。分離器負責對監聽IO事件,並通知處理器;處理器負責對IO內容進行處理,完成對應的業務。

二者的差異,以讀操作為例(寫操作類似)。

Reactor中實現讀:

1.        註冊讀就緒事件和相應的事件處理器。

2.        事件分離器等待事件。

3.        事件到來,激活分離器,分離器調用對應的處理器。

4.        處理器完成IO讀操作,處理讀到的數據,註冊新的事件,然後返還控制權。

Proactor中實現讀:

1.        註冊讀完成事件和相應的事件處理器(包括緩衝區地址)。

2.        事件分離器等待操作完成事件的同時,操作系統利用並行的內核線程執行實際的讀操作,並將結果數據存入用戶自定義緩衝區,最後通知事件分離器讀操作完成。

3.        事件分離器呼喚處理器。

4.        事件處理器處理用戶自定義緩衝區中的數據,然後啟動一個新的異步操作,並將控制權返回事件分離器。

由此可見,兩者的主要區別:Reactor中,由用戶線程(事件處理器所在線程)完成IO的讀寫操作;而在Proactor中,是由操作系統完成IO讀寫操作後,再通知事件處理器,用戶線程只完成對數據的業務邏輯處理部分。

更多問題可以問遠標教育中心的技術諮詢。

Java中BIO,NIO和AIO的區別和應用場景

Reactor and Proactor

IO讀寫時,多路復用機制都會依賴對一個事件多路分離器,負責把源事件的IO 事件分離出來,分別到相應的read/write事件分離器。涉及到事件分離器的兩種模式分別就是 Reactor和Proactor,Reactor是基於同步IO的,Proactor是基於異步IO的。

在Reactor模式中,事件分離者等待某個事件或者可應用或個操作的狀態發生(比如文件描述符可讀寫,或者是socket可讀寫),事件分離者就把這個事件傳給事先註冊的事件處理函數或者回調函數,由後者來做實際的讀寫操作。

013 BIO、NIO、AIO的區別

IO通常分為幾種,BIO(阻塞 Blocking IO)、NIO(非阻塞 Non-Blocking IO)、AIO(異步非阻塞)。

在JDK1.4出來之前,我們建立網絡連接的時候採用BIO模式,需要先在服務端啟動一個ServerSocket,然後在客戶端啟動Socket來對服務端進行通信,默認情況下服務端需要建立一堆線程等待請求,而客戶端發送請求後,先詢問服務端是否有線程響應,如果沒有則會一直等待或者遭到拒絕請求。BIO模型圖如下:

優缺點很明顯。這裡主要說下缺點:主要瓶頸在線程上。每個連接都會建立一個線程。雖然線程消耗比進程小,但是一台機器實際上能建立的有效線程有限,以Java來說,1.5以後,一個線程大致消耗1M內存!且隨着線程數量的增加,CPU切換線程上下文的消耗也隨之增加,在高過某個閥值後,繼續增加線程,性能不增反降!而同樣因為一個連接就新建一個線程,所以編碼模型很簡單!

就性能瓶頸這一點,就確定了BIO並不適合進行高性能服務器的開發!像Tomcat這樣的Web服務器,從7開始就從BIO改成了NIO,來提高服務器性能!

NIO本身是基於事件驅動思想來完成的,其主要想解決的是BIO的大並發問題:在使用同步I/O的網絡應用中,如果要同時處理多個客戶端請求,就必須使用多線程來處理。也就是說,將每一個客戶端請求分配給一個線程來單獨處理。這樣做雖然可以達到我們的要求,但同時又會帶來另外一個問題。由於每創建一個線程,就要為這個線程分配一定的內存空間(也叫工作存儲器),而且操作系統本身也對線程的總數有一定的限制。如果客戶端過多,服務端程序可能會因為不堪重負而拒絕客戶端的請求,甚至服務器可能會因此而癱瘓。

NIO基於Reactor,當socket有流可讀或可寫入socket時,操作系統會相應的通知應用程序進行處理,應用再將流讀取到緩衝區或寫入操作系統。 也就是說,這個時候,已經不是一個連接就要對應一個處理線程了,而是有效的請求,對應一個線程,當連接沒有數據時,是沒有工作線程來處理的。

BIO與NIO一個比較重要的不同,是我們使用BIO的時候往往會引入多線程,每個連接一個單獨的線程;而NIO則是使用單線程或者只使用少量的多線程。NIO模型圖如下:

NIO的優缺點和BIO就完全相反了!性能高,不用一個連接就建一個線程,可以一個線程處理所有的連接!相應的,編碼就複雜很多。還有一個問題,由於是非阻塞的,應用無法知道什麼時候消息讀完!

BIO和NIO的對比圖:

AIO沒有前兩者普及,暫不討論!

阻塞、非阻塞、多路復用、同步、異步、BIO、NIO、AIO 一文搞定

關於IO會涉及到阻塞、非阻塞、多路復用、同步、異步、BIO、NIO、AIO等幾個知識點。知識點雖然不難但平常經常容易搞混,特此Mark下,與君共勉。

阻塞IO情況下,當用戶調用 read 後,用戶線程會被阻塞,等內核數據準備好並且數據從內核緩衝區拷貝到用戶態緩存區後 read 才會返回。可以看到是阻塞的兩個部分。

非阻塞IO發出read請求後發現數據沒準備好,會繼續往下執行,此時應用程序會不斷輪詢polling內核詢問數據是否準備好,當數據沒有準備好時,內核立即返回EWOULDBLOCK錯誤。直到數據被拷貝到應用程序緩衝區,read請求才獲取到結果。並且你要注意!這裡最後一次 read 調用獲取數據的過程,是一個同步的過程,是需要等待的過程。這裡的同步指的是 內核態的數據拷貝到用戶程序的緩存區這個過程 。

非阻塞情況下無可用數據時,應用程序每次輪詢內核看數據是否準備好了也耗費CPU,能否不讓它輪詢,當內核緩衝區數據準備好了,以事件通知當機制告知應用進程數據準備好了呢?應用進程在沒有收到數據準備好的事件通知信號時可以忙寫其他的工作。此時 IO多路復用 就派上用場了。

IO多路復用中文比較讓人頭大,IO多路復用的原文叫 I/O multiplexing,這裡的 multiplexing 指的其實是在單個線程通過記錄跟蹤每一個Sock(I/O流)的狀態來同時管理多個I/O流. 發明它的目的是盡量多的提高服務器的吞吐能力。實現一個線程監控多個IO請求,哪個IO有請求就把數據從內核拷貝到進程緩衝區,拷貝期間是阻塞的!現在已經可以通過採用mmap地址映射的方法,達到內存共享效果,避免真複製,提高效率。

像 select、poll、epoll 都是I/O多路復用的具體的實現。

select是第一版IO復用,提出後暴漏了很多問題。

poll 修復了 select 的很多問題。

但是poll仍然不是線程安全的, 這就意味着不管服務器有多強悍,你也只能在一個線程裡面處理一組 I/O 流。你當然可以拿多進程來配合了,不過然後你就有了多進程的各種問題。

epoll 可以說是 I/O 多路復用最新的一個實現,epoll 修復了poll 和select絕大部分問題, 比如:

橫軸 Dead connections 是鏈接數的意思,叫這個名字只是它的測試工具叫deadcon。縱軸是每秒處理請求的數量,可看到epoll每秒處理請求的數量基本不會隨着鏈接變多而下降的。poll 和/dev/poll 就很慘了。但 epoll 有個致命的缺點是只有 linux 支持。

比如平常Nginx為何可以支持4W的QPS是因為它會使用目標平台上面最高效的I/O多路復用模型。

然後你會發現上面的提到過的操作都不是真正的異步,因為兩個階段總要等待會兒!而真正的異步 I/O 是內核數據準備好和數據從內核態拷貝到用戶態這兩個過程都不用等待。

很慶幸,Linux給我們準備了 aio_read 跟 aio_write 函數實現真實的異步,當用戶發起aio_read請求後就會自動返回。內核會自動將數據從內核緩衝區拷貝到用戶進程空間,應用進程啥都不用管。

我強力推薦C++後端開發免費學習地址:C/C++Linux服務器開發/後台架構師【零聲教育】-學習視頻教程-騰訊課堂

同步跟異步的區別在於 數據從內核空間拷貝到用戶空間是否由用戶線程完成 ,這裡又分為同步阻塞跟同步非阻塞兩種。

我們以同步非阻塞為例,如下可看到,在將數據從內核拷貝到用戶空間這一過程,是由用戶線程阻塞完成的。

可發現,用戶在調用之後會立即返回,由內核完成數據的拷貝工作,並通知用戶線程,進行回調。

在Java中,我們使用socket進行網絡通信,IO主要有三種模式,主要看 內核支持 哪些。

同步阻塞IO ,每個客戶端的Socket連接請求,服務端都會對應有個處理線程與之對應,對於沒有分配到處理線程的連接就會被阻塞或者拒絕。相當於是 一個連接一個線程 。

BIO特點 :

常量:

主類:

服務端監聽線程:

服務端處理線程:

客戶端:

同步非阻塞IO之NIO :服務器端保存一個Socket連接列表,然後對這個列表進行輪詢,如果發現某個Socket端口上有數據可讀時說明讀就緒,則調用該socket連接的相應讀操作。如果發現某個 Socket端口上有數據可寫時說明寫就緒,則調用該socket連接的相應寫操作。如果某個端口的Socket連接已經中斷,則調用相應的析構方法關閉該端口。這樣能充分利用服務器資源,效率得到了很大提高,在進行IO操作請求時候再用個線程去處理,是 一個請求一個線程 。Java中使用Selector、Channel、Buffer來實現上述效果。

每個線程中包含一個 Selector 對象,它相當於一個通道管理器,可以實現在一個線程中處理多個通道的目的,減少線程的創建數量。遠程連接對應一個channel,數據的讀寫通過buffer均在同一個 channel 中完成,並且數據的讀寫是非阻塞的。通道創建後需要註冊在 selector 中,同時需要為該通道註冊感興趣事件(客戶端連接服務端事件、服務端接收客戶端連接事件、讀事件、寫事件), selector 線程需要採用 輪訓 的方式調用 selector 的 select 函數,直到所有註冊通道中有興趣的事件發生,則返回,否則一直阻塞。而後循環處理所有就緒的感興趣事件。以上步驟解決BIO的兩個瓶頸:

下面對以下三個概念做一個簡單介紹,Java NIO由以下三個核心部分組成:

channel和buffer有好幾種類型。下面是Java NIO中的一些主要channel的實現:

正如你所看到的,這些通道涵蓋了UDP和TCP網絡IO,以及文件IO。以下是Java NIO里關鍵的buffer實現:

在微服務階段,一個請求可能涉及到多個不同服務之間的跨服務器調用,如果你想實現高性能的PRC框架來進行數據傳輸,那就可以基於Java NIO做個支持長連接、自定義協議、高並發的框架,比如Netty。Netty本身就是一個基於NIO的網絡框架, 封裝了Java NIO那些複雜的底層細節,給你提供簡單好用的抽象概念來編程。比如Dubbo底層就是用的Netty。

AIO是異步非阻塞IO,相比NIO更進一步,進程讀取數據時只負責發送跟接收指令,數據的準備工作完全由操作系統來處理。

推薦一個零聲教育C/C++後台開發的免費公開課程,個人覺得老師講得不錯,分享給大家:C/C++後台開發高級架構師,內容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等技術內容,C/C++Linux服務器開發/後台架構師【零聲教育】-學習視頻教程-騰訊課堂 立即學習

原文:阻塞、非阻塞、多路復用、同步、異步、BIO、NIO、AIO 一鍋端

原創文章,作者:TRWF,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/137765.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
TRWF的頭像TRWF
上一篇 2024-10-04 00:18
下一篇 2024-10-04 00:18

相關推薦

  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean加載過程

    Java Bean加載過程涉及到類加載器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean加載的過程。 一、類加載器 類加載器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字符串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字符串中是否存在多個指定字符: 一、字符串遍歷 字符串是Java編程中非常重要的一種數據類型。要判斷字符串中是否存在多個指定字符…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Java 8 Group By 會影響排序嗎?

    是的,Java 8中的Group By會對排序產生影響。本文將從多個方面探討Group By對排序的影響。 一、Group By的概述 Group By是SQL中的一種常見操作,它…

    編程 2025-04-29

發表回復

登錄後才能評論