日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人工智能 > ChatGpt >内容正文

ChatGpt

什么是 NIO? NIO 和 BIO、AIO 之间的区别是什么?NIO主要用来解决什么问题?

發(fā)布時(shí)間:2025/3/12 ChatGpt 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 什么是 NIO? NIO 和 BIO、AIO 之间的区别是什么?NIO主要用来解决什么问题? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 BIO,NIO,AIO都有什么區(qū)別,NIO的原理是什么?

BIO

BIO:傳統(tǒng)的網(wǎng)絡(luò)通訊模型,就是BIO,同步阻塞IO, 其實(shí)就是服務(wù)端創(chuàng)建一個(gè)ServerSocket, 然后就是客戶端用一個(gè)Socket去連接服務(wù)端的那個(gè)ServerSocket, ServerSocket接收到了一個(gè)的連接請(qǐng)求就創(chuàng)建一個(gè)Socket和一個(gè)線程去跟那個(gè)Socket進(jìn)行通訊。接著客戶端和服務(wù)端就進(jìn)行阻塞式的通信,客戶端發(fā)送一個(gè)請(qǐng)求,服務(wù)端Socket進(jìn)行處理后返回響應(yīng),在響應(yīng)返回前,客戶端那邊就阻塞等待,什么事情也做不了。 這種方式的缺點(diǎn), 每次一個(gè)客戶端接入,都需要在服務(wù)端創(chuàng)建一個(gè)線程來(lái)服務(wù)這個(gè)客戶端,這樣大量客戶端來(lái)的時(shí)候,就會(huì)造成服務(wù)端的線程數(shù)量可能達(dá)到了幾千甚至幾萬(wàn),這樣就可能會(huì)造成服務(wù)端過(guò)載過(guò)高,最后崩潰死掉。
————————————————
BIO模型圖:

Acceptor:

傳統(tǒng)的IO模型的網(wǎng)絡(luò)服務(wù)的設(shè)計(jì)模式中有倆種比較經(jīng)典的設(shè)計(jì)模式: 一個(gè)是多線程, 一種是依靠線程池來(lái)進(jìn)行處理。如果是基于多線程的模式來(lái)的話,就是這樣的模式,這種也是

Acceptor線程模型。

NIO

NIO: NIO是一種同步非阻塞IO, 基于Reactor模型來(lái)實(shí)現(xiàn)的。其實(shí)相當(dāng)于就是一個(gè)線程處理大量的客戶端的請(qǐng)求,通過(guò)一個(gè)線程輪詢大量的channel,每次就獲取一批有事件的channel,然后對(duì)每個(gè)請(qǐng)求啟動(dòng)一個(gè)線程處理即可。這里的核心就是非阻塞,就那個(gè)selector一個(gè)線程就可以不停輪詢channel,所有客戶端請(qǐng)求都不會(huì)阻塞,直接就會(huì)進(jìn)來(lái),大不了就是等待一下排著隊(duì)而已。這里面優(yōu)化BIO的核心就是,一個(gè)客戶端并不是時(shí)時(shí)刻刻都有數(shù)據(jù)進(jìn)行交互,沒(méi)有必要死耗著一個(gè)線程不放,所以客戶端選擇了讓線程歇一歇,只有客戶端有相應(yīng)的操作的時(shí)候才發(fā)起通知,創(chuàng)建一個(gè)線程來(lái)處理請(qǐng)求。
————————————————
NIO:模型圖

Reactor模型:

AIO

AIO:異步非阻塞IO,基于Proactor模型實(shí)現(xiàn)。 每個(gè)連接發(fā)送過(guò)來(lái)的請(qǐng)求,都會(huì)綁定一個(gè)Buffer,然后通知操作系統(tǒng)去完成異步的讀,這個(gè)時(shí)間你就可以去做其他的事情,等到操作系統(tǒng)完成讀之后,就會(huì)調(diào)用你的接口,給你操作系統(tǒng)異步讀完的數(shù)據(jù)。這個(gè)時(shí)候你就可以拿到數(shù)據(jù)進(jìn)行處理,將數(shù)據(jù)往回寫(xiě),在往回寫(xiě)的過(guò)程,同樣是給操作系統(tǒng)一個(gè)Buffer,讓操作系統(tǒng)去完成寫(xiě),寫(xiě)完了來(lái)通知你。這倆個(gè)過(guò)程都有buffer存在,數(shù)據(jù)都是通過(guò)buffer來(lái)完成讀寫(xiě)。

這里面的主要的區(qū)別在于將數(shù)據(jù)寫(xiě)入的緩沖區(qū)后,就不去管它,剩下的去交給操作系統(tǒng)去完成。操作系統(tǒng)寫(xiě)回?cái)?shù)據(jù)也是一樣,寫(xiě)到Buffer里面,寫(xiě)完后通知客戶端來(lái)進(jìn)行讀取數(shù)據(jù)。
————————————————
AIO:模型圖

聊完了BIO,NIO,AIO的區(qū)別之后,現(xiàn)在我們?cè)俳Y(jié)合這三個(gè)模型來(lái)說(shuō)下同步和阻塞的一些問(wèn)題。

2 各種阻塞解釋

同步阻塞

同步阻塞:為什么說(shuō)BIO是同步阻塞的呢?其實(shí)這里說(shuō)的不是針對(duì)網(wǎng)絡(luò)通訊模型而言,而是針對(duì)磁盤文件讀寫(xiě)IO操作來(lái)說(shuō)的。因?yàn)橛肂IO的流讀寫(xiě)文件,例如FileInputStrem,是說(shuō)你發(fā)起個(gè)IO請(qǐng)求直接hang死,卡在那里,必須等著搞完了這次IO才能返回。

同步非阻塞:

同步非阻塞:為什么說(shuō)NIO為啥是同步非阻塞?因?yàn)闊o(wú)論多少客戶端都可以接入服務(wù)端,客戶端接入并不會(huì)耗費(fèi)一個(gè)線程,只會(huì)創(chuàng)建一個(gè)連接然后注冊(cè)到selector上去罷了,你就可以去干其他你想干的其他事情了, 一個(gè)selector線程不斷的輪詢所有的socket連接,發(fā)現(xiàn)有事件了就通知你,然后你就啟動(dòng)一個(gè)線程處理一個(gè)請(qǐng)求即可,這個(gè)過(guò)程的話就是非阻塞的。但是這個(gè)處理的過(guò)程中,你還是要先讀取數(shù)據(jù),處理,再返回的,這是個(gè)同步的過(guò)程。

異步非阻塞

異步非阻塞:為什么說(shuō)AIO是異步非阻塞?通過(guò)AIO發(fā)起個(gè)文件IO操作之后,你立馬就返回可以干別的事兒了,接下來(lái)你也不用管了,操作系統(tǒng)自己干完了IO之后,告訴你說(shuō)ok了, 當(dāng)你基于AIO的api去都寫(xiě)文件的時(shí)候, 當(dāng)你發(fā)起一個(gè)請(qǐng)求之后,剩下的事情就是交給了操作系統(tǒng),當(dāng)讀寫(xiě)完成后, 操作系統(tǒng)會(huì)來(lái)回調(diào)你的接口, 告訴你操作完成, 在這期間不需要等待, 也不需要去輪詢判斷操作系統(tǒng)完成的狀態(tài),你可以去干其他的事情。 同步就是自己還得主動(dòng)去輪詢操作系統(tǒng),異步就是操作系統(tǒng)反過(guò)來(lái)通知你。所以來(lái)說(shuō), AIO就是異步非阻塞的。

3 NIO核心組件詳細(xì)講解

學(xué)習(xí)NIO先來(lái)搞清楚一些相關(guān)的概念,NIO通訊有哪些相關(guān)組件,對(duì)應(yīng)的作用都是什么,之間有哪些聯(lián)系?

多路復(fù)用機(jī)制實(shí)現(xiàn)Selector

首先我們來(lái)了解下傳統(tǒng)的Socket網(wǎng)絡(luò)通訊模型。

傳統(tǒng)Socket通訊原理圖

為什么傳統(tǒng)的socket不支持海量連接

每次一個(gè)客戶端接入,都是要在服務(wù)端創(chuàng)建一個(gè)線程來(lái)服務(wù)這個(gè)客戶端的,這會(huì)導(dǎo)致大量的客戶端的時(shí)候,服務(wù)端的線程數(shù)量可能達(dá)到幾千甚至幾萬(wàn),幾十萬(wàn),這會(huì)導(dǎo)致服務(wù)器端程序負(fù)載過(guò)高,不堪重負(fù),最終系統(tǒng)崩潰死掉。

  • 接著來(lái)看下NIO是如何基于Selector實(shí)現(xiàn)多路復(fù)用機(jī)制支持的海量連接。

NIO原理圖

多路復(fù)用機(jī)制是如何支持海量連接

NIO的線程模型 對(duì)Socket發(fā)起的連接不需要每個(gè)都創(chuàng)建一個(gè)線程,完全可以使用一個(gè)Selector來(lái)多路復(fù)用監(jiān)聽(tīng)N多個(gè)Channel是否有請(qǐng)求,該請(qǐng)求是對(duì)應(yīng)的連接請(qǐng)求,還是發(fā)送數(shù)據(jù)的請(qǐng)求,這里面是基于操作系統(tǒng)底層的Select通知機(jī)制的,一個(gè)Selector不斷的輪詢多個(gè)Channel,這樣避免了創(chuàng)建多個(gè)線程,只有當(dāng)莫個(gè)Channel有對(duì)應(yīng)的請(qǐng)求的時(shí)候才會(huì)創(chuàng)建線程,可能說(shuō)1000個(gè)請(qǐng)求, 只有100個(gè)請(qǐng)求是有數(shù)據(jù)交互的, 這個(gè)時(shí)候可能server端就提供10個(gè)線程就能夠處理這些請(qǐng)求。這樣的話就可以避免了創(chuàng)建大量的線程。

NIO如何通過(guò)Buffer來(lái)緩沖數(shù)據(jù)的

NIO中的Buffer是個(gè)什么東西 ?

學(xué)習(xí)NIO,首當(dāng)其沖就是要了解所謂的Buffer緩沖區(qū),這個(gè)東西是NIO里比較核心的一個(gè)部分,一般來(lái)說(shuō),如果你要通過(guò)NIO寫(xiě)數(shù)據(jù)到文件或者網(wǎng)絡(luò),或者是從文件和網(wǎng)絡(luò)讀取數(shù)據(jù)出來(lái)此時(shí)就需要通過(guò)Buffer緩沖區(qū)來(lái)進(jìn)行。Buffer的使用一般有如下幾個(gè)步驟:

寫(xiě)入數(shù)據(jù)到Buffer,調(diào)用flip()方法,從Buffer中讀取數(shù)據(jù),調(diào)用clear()方法或者compact()方法。

Buffer中對(duì)應(yīng)的Position, Mark, Capacity,Limit都啥?

capacity: 緩沖區(qū)容量的大小,就是里面包含的數(shù)據(jù)大小。
limit: 對(duì)buffer緩沖區(qū)使用的一個(gè)限制,從這個(gè)index開(kāi)始就不能讀取數(shù)據(jù)了。
position: 代表著數(shù)組中可以開(kāi)始讀寫(xiě)的index, 不能大于limit。
mark: 是類似路標(biāo)的東西,在某個(gè)position的時(shí)候,設(shè)置一下mark,此時(shí)就可以設(shè)置一個(gè)標(biāo)記,后續(xù)調(diào)用reset()方法可以把position復(fù)位到當(dāng)時(shí)設(shè)置的那個(gè)mark上去,把position或limit調(diào)整為小于mark的值時(shí),就丟棄這個(gè)mark。如果使用的是Direct模式創(chuàng)建的Buffer的話,就會(huì)減少中間緩沖直接使用的是DirectorBuffer來(lái)進(jìn)行數(shù)據(jù)的存儲(chǔ)。
————————————————

如何通過(guò)Channel和FileChannel讀取Buffer數(shù)據(jù)寫(xiě)入磁盤的

NIO中,Channel是什么?

Channel是NIO中的數(shù)據(jù)通道,類似流,但是又有些不同,Channel即可從中讀取數(shù)據(jù),又可以從寫(xiě)數(shù)據(jù)到通道中,但是流的讀寫(xiě)通常是單向的。Channel可以異步的讀寫(xiě)。Channel中的數(shù)據(jù)總是要先讀到一個(gè)Buffer中,或者從緩沖區(qū)中將數(shù)據(jù)寫(xiě)到通道中。

FileChannel的作用是什么 Buffer有不同的類型,同樣Channel也有好幾個(gè)類型。 FileChannel,DatagramChannel,SocketChannel,ServerSocketChannel。這些通道涵蓋了UDP 和 TCP 網(wǎng)絡(luò)IO,以及文件IO。而FileChannel就是文件IO對(duì)應(yīng)的管道, 在讀取文件的時(shí)候會(huì)用到這個(gè)管道。

4 問(wèn)題(代碼實(shí)現(xiàn))

BIO 和 NIO 作為 Server 端,當(dāng)建立了 10 個(gè)連接時(shí),分別產(chǎn)生多少個(gè)線程?

答案: 因?yàn)閭鹘y(tǒng)的 IO 也就是 BIO 是同步線程堵塞的,所以每個(gè)連接都要分配一個(gè)專用線程來(lái)處理請(qǐng)求,這樣 10 個(gè)連接就會(huì)創(chuàng)建 10 個(gè)線程去處理。而 NIO 是一種同步非阻塞的 I/O 模型,它的核心技術(shù)是多路復(fù)用,可以使用一個(gè)鏈接上的不同通道來(lái)處理不同的請(qǐng)求,所以即使有 10 個(gè)連接,對(duì)于 NIO 來(lái)說(shuō),開(kāi)啟 1 個(gè)線程就夠了。
————————————————

BIO 代碼實(shí)現(xiàn)

publicclassDemoServerextendsThread{privateServerSocket serverSocket;publicint getPort(){return serverSocket.getLocalPort();}publicvoid run(){try{serverSocket =newServerSocket(0);while(true){Socket socket = serverSocket.accept();RequestHandler requestHandler =newRequestHandler(socket);requestHandler.start();}}catch(IOException e){e.printStackTrace();}finally{if(serverSocket !=null){try{serverSocket.close();}catch(IOException e){e.printStackTrace();}}}}publicstaticvoid main(String[] args)throwsIOException{DemoServer server =newDemoServer();server.start();try(Socket client =newSocket(InetAddress.getLocalHost(), server.getPort())){BufferedReader bufferedReader =newBufferedReader(newInputStreamReader(client.getInputStream()));bufferedReader.lines().forEach(s ->System.out.println(s));}}} // 簡(jiǎn)化實(shí)現(xiàn),不做讀取,直接發(fā)送字符串classRequestHandlerextendsThread{privateSocket socket;RequestHandler(Socket socket){this.socket = socket;}@Overridepublicvoid run(){try(PrintWriter out =newPrintWriter(socket.getOutputStream());){out.println("Hello world!");out.flush();}catch(Exception e){e.printStackTrace();}}}

服務(wù)器端啟動(dòng) ServerSocket,端口 0 表示自動(dòng)綁定一個(gè)空閑端口。
調(diào)用 accept 方法,阻塞等待客戶端連接。
利用 Socket 模擬了一個(gè)簡(jiǎn)單的客戶端,只進(jìn)行連接、讀取、打印。
當(dāng)連接建立后,啟動(dòng)一個(gè)單獨(dú)線程負(fù)責(zé)回復(fù)客戶端請(qǐng)求。
這樣,一個(gè)簡(jiǎn)單的 Socket 服務(wù)器就被實(shí)現(xiàn)出來(lái)了。
————————————————

NIO 代碼實(shí)現(xiàn)

publicclassNIOServerextendsThread{publicvoid run(){try(Selector selector =Selector.open();ServerSocketChannel serverSocket =ServerSocketChannel.open();){// 創(chuàng)建 Selector 和 ChannelserverSocket.bind(newInetSocketAddress(InetAddress.getLocalHost(),8888));serverSocket.configureBlocking(false); // 注冊(cè)到 Selector,并說(shuō)明關(guān)注點(diǎn)serverSocket.register(selector,SelectionKey.OP_ACCEPT);while(true){selector.select();// 阻塞等待就緒的 Channel,這是關(guān)鍵點(diǎn)之一Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iter = selectedKeys.iterator();while(iter.hasNext()){SelectionKey key = iter.next(); // 生產(chǎn)系統(tǒng)中一般會(huì)額外進(jìn)行就緒狀態(tài)檢查sayHelloWorld((ServerSocketChannel) key.channel());iter.remove();}}}catch(IOException e){e.printStackTrace();}}privatevoid sayHelloWorld(ServerSocketChannel server)throwsIOException{try(SocketChannel client = server.accept();){ client.write(Charset.defaultCharset().encode("Hello world!"));}} // 省略了與前面類似的 main}

首先,通過(guò) Selector.open() 創(chuàng)建一個(gè) Selector,作為類似調(diào)度員的角色。
然后,創(chuàng)建一個(gè) ServerSocketChannel,并且向 Selector 注冊(cè),通過(guò)指定 SelectionKey.OP_ACCEPT,告訴調(diào)度員,它關(guān)注的是新的連接請(qǐng)求。注意:為什么我們要明確配置非阻塞模式呢?這是因?yàn)樽枞J较?#xff0c;注冊(cè)操作是不允許的,會(huì)拋出 IllegalBlockingModeException 異常。
Selector 阻塞在 select 操作,當(dāng)有 Channel 發(fā)生接入請(qǐng)求,就會(huì)被喚醒。
在 sayHelloWorld 方法中,通過(guò) SocketChannel 和 Buffer 進(jìn)行數(shù)據(jù)操作,在本例中是發(fā)送了一段字符串。
可以看到,在前面兩個(gè)樣例中,IO 都是同步阻塞模式,所以需要多線程以實(shí)現(xiàn)多任務(wù)處理。而 NIO 則是利用了單線程輪詢事件的機(jī)制,通過(guò)高效地定位就緒的 Channel,來(lái)決定做什么,僅僅 select 階段是阻塞的,可以有效避免大量客戶端連接時(shí),頻繁線程切換帶來(lái)的問(wèn)題,應(yīng)用的擴(kuò)展能力有了非常大的提高。

6 NIO與IO的區(qū)別

NIO即New IO,這個(gè)庫(kù)是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實(shí)現(xiàn)方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多。在Java API中提供了兩套NIO,一套是針對(duì)標(biāo)準(zhǔn)輸入輸出NIO,另一套就是網(wǎng)絡(luò)編程N(yùn)IO。
NIO和IO的主要區(qū)別,下表總結(jié)了Java IO和NIO之間的主要區(qū)別:
————————————————

1、面向流與面向緩沖
Java IO和NIO之間第一個(gè)最大的區(qū)別是,IO是面向流的,NIO是面向緩沖區(qū)的。 Java IO面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié),直至讀取所有字節(jié),它們沒(méi)有被緩存在任何地方。此外,它不能前后移動(dòng)流中的數(shù)據(jù)。如果需要前后移動(dòng)從流中讀取的數(shù)據(jù),需要先將它緩存到一個(gè)緩沖區(qū)。Java NIO的緩沖導(dǎo)向方法略有不同。數(shù)據(jù)讀取到一個(gè)它稍后處理的緩沖區(qū),需要時(shí)可在緩沖區(qū)中前后移動(dòng)。這就增加了處理過(guò)程中的靈活性。但是,還需要檢查是否該緩沖區(qū)中包含所有您需要處理的數(shù)據(jù)。而且,需確保當(dāng)更多的數(shù)據(jù)讀入緩沖區(qū)時(shí),不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。

2、阻塞與非阻塞IO
Java IO的各種流是阻塞的。這意味著,當(dāng)一個(gè)線程調(diào)用read() 或 write() 時(shí),該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫(xiě)入。該線程在此期間不能再干任何事情了。Java NIO的非阻塞模式,使一個(gè)線程從某通道發(fā)送請(qǐng)求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒(méi)有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取,而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情。非阻塞寫(xiě)也是如此。一個(gè)線程請(qǐng)求寫(xiě)入一些數(shù)據(jù)到某通道,但不需要等待它完全寫(xiě)入,這個(gè)線程同時(shí)可以去做別的事情。線程通常將非阻塞IO的空閑時(shí)間用于在其它通道上執(zhí)行IO操作,所以一個(gè)單獨(dú)的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道(channel)。

3、選擇器(Selectors)
Java NIO的選擇器允許一個(gè)單獨(dú)的線程來(lái)監(jiān)視多個(gè)輸入通道,你可以注冊(cè)多個(gè)通道使用一個(gè)選擇器,然后使用一個(gè)單獨(dú)的線程來(lái)“選擇”通道:這些通道里已經(jīng)有可以處理的輸入,或者選擇已準(zhǔn)備寫(xiě)入的通道。這種選擇機(jī)制,使得一個(gè)單獨(dú)的線程很容易來(lái)管理多個(gè)通道。

7 NIO和IO適用場(chǎng)景

NIO是為彌補(bǔ)傳統(tǒng)IO的不足而誕生的,但是尺有所短寸有所長(zhǎng),**NIO也有缺點(diǎn),因?yàn)镹IO是面向緩沖區(qū)的操作,每一次的數(shù)據(jù)處理都是對(duì)緩沖區(qū)進(jìn)行的,那么就會(huì)有一個(gè)問(wèn)題,在數(shù)據(jù)處理之前必須要判斷緩沖區(qū)的數(shù)據(jù)是否完整或者已經(jīng)讀取完畢,如果沒(méi)有,假設(shè)數(shù)據(jù)只讀取了一部分,那么對(duì)不完整的數(shù)據(jù)處理沒(méi)有任何意義。**所以每次數(shù)據(jù)處理之前都要檢測(cè)緩沖區(qū)數(shù)據(jù)。
那么NIO和IO各適用的場(chǎng)景是什么呢?
如果需要管理同時(shí)打開(kāi)的成千上萬(wàn)個(gè)連接,這些連接每次只是發(fā)送少量的數(shù)據(jù),例如聊天服務(wù)器,這時(shí)候用NIO處理數(shù)據(jù)可能是個(gè)很好的選擇。
而如果只有少量的連接,而這些連接每次要發(fā)送大量的數(shù)據(jù),這時(shí)候傳統(tǒng)的IO更合適。使用哪種處理數(shù)據(jù),需要在數(shù)據(jù)的響應(yīng)等待時(shí)間和檢查緩沖區(qū)數(shù)據(jù)的時(shí)間上作比較來(lái)權(quán)衡選擇。

8 參考鏈接

https://blog.csdn.net/valada/article/details/96040288

https://blog.csdn.net/weixin_44195108/article/details/88640803

總結(jié)

以上是生活随笔為你收集整理的什么是 NIO? NIO 和 BIO、AIO 之间的区别是什么?NIO主要用来解决什么问题?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。