国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

這篇具有很好參考價(jià)值的文章主要介紹了Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

系列文章目錄和關(guān)于我

零丶背景

最近有很多想學(xué)的,像netty的使用、原理源碼,但是苦于自己對(duì)于操作系統(tǒng)和nio了解不多,有點(diǎn)無從下手,遂學(xué)習(xí)之。

一丶網(wǎng)絡(luò)io的過程

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

上圖粗略描述了網(wǎng)絡(luò)io的過程,了解其中的拷貝過程有利于我們理解非阻塞io,以及IO多路復(fù)用的必要性。

  1. 數(shù)據(jù)從網(wǎng)卡到內(nèi)核緩沖區(qū)
    網(wǎng)卡通過DMA的方式將網(wǎng)絡(luò)幀copy到內(nèi)核空間

    并不是拷貝到內(nèi)核空間就完事了,因?yàn)檫€需要根據(jù)協(xié)議對(duì)數(shù)據(jù)進(jìn)行處理。

    所以網(wǎng)卡使用硬中斷通知cpu,cpu響應(yīng)后會(huì)使用網(wǎng)卡注冊(cè)函數(shù)進(jìn)行收包,然后協(xié)議層處理網(wǎng)絡(luò)幀。

  2. 數(shù)據(jù)從內(nèi)核緩沖區(qū)到用戶空間

    根據(jù)協(xié)議處理好的數(shù)據(jù),還需要拷貝到用戶空間才能被運(yùn)行在內(nèi)核態(tài)的應(yīng)用程序使用==>cpu進(jìn)行數(shù)據(jù)拷貝。隨后內(nèi)核喚醒用戶進(jìn)程,相當(dāng)于我們的java程序從阻塞io中被喚醒,繼續(xù)執(zhí)行下一行代碼的執(zhí)行。

二丶Socket通信過程與其中的阻塞點(diǎn)

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

這其中有幾個(gè)阻塞的過程

  • accept 系統(tǒng)調(diào)用:等待客戶端建立tcp連接

    這個(gè)問題不大,沒有連接那么阻塞服務(wù)端線程,可以節(jié)約cpu資源。

  • read系統(tǒng)調(diào)用:等待請(qǐng)求數(shù)據(jù)來到用戶空間

    數(shù)據(jù)從網(wǎng)卡到用戶空間的過程,線程時(shí)阻塞的

  • Servlet#service 處理請(qǐng)求是一個(gè)同步過程

    tomcat根據(jù)http協(xié)議構(gòu)造request,并和response作為參數(shù),找到對(duì)應(yīng)Servlet調(diào)用service方法,Servlet#service方法執(zhí)行結(jié)束,返回內(nèi)容才能通過write系統(tǒng)調(diào)用回應(yīng)數(shù)據(jù)。

    這導(dǎo)致在業(yè)務(wù)處理上需要使用線程池來讓服務(wù)端可以處理多個(gè)并發(fā)請(qǐng)求。

  • write系統(tǒng)調(diào)用:響應(yīng)數(shù)據(jù)寫回

    write系統(tǒng)調(diào)用將servlet處理后的響應(yīng)數(shù)據(jù),寫回到文件描述符中。

三丶NIO解決了什么問題

1.單線程監(jiān)測(cè)若干個(gè)文件描述符是否可以執(zhí)行IO操作

這就是常說的IO多路復(fù)用,那為什么需要IO多路復(fù)用?

盡量使用較少的系統(tǒng)資源處理更多的連接,如果當(dāng)前單臺(tái)服務(wù)器接收了1w個(gè)請(qǐng)求,服務(wù)端當(dāng)如何處理?

1.1 傳統(tǒng)BIO模型

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

上面是一段java BIO模型并發(fā)處理多請(qǐng)求的實(shí)例代碼,它有以下不足

  • 大量的線程占用很大的內(nèi)存空間
  • 線程切換會(huì)帶來很大的開銷
  • process方法中需要需要調(diào)用read系統(tǒng)調(diào)用,阻塞直到可讀,并沒有真正進(jìn)行讀寫操作。

1.2. 非阻塞IO

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

上面是非阻塞IO的一個(gè)實(shí)例

socketChannel.configureBlocking(false)可以讓后續(xù)的read在通道數(shù)據(jù)沒有就緒的時(shí)候直接返回-1,而不是讓線程阻塞。這個(gè)特性讓調(diào)度線程池中的線程減少了阻塞,從而節(jié)省了線程資源。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

但是這種方式也不是沒有任何缺點(diǎn),多次系統(tǒng)意味著多次系統(tǒng)調(diào)用,每次系統(tǒng)調(diào)用都需要,用戶態(tài)<=>內(nèi)核態(tài)的來回切換,需要cpu保存進(jìn)程的上下文,調(diào)用結(jié)束還需要恢復(fù)進(jìn)程的上下文。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

1.3 IO多路復(fù)用

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

如上是Java IO多路復(fù)用的簡(jiǎn)陋例子。操作系統(tǒng)提供了多路復(fù)用的機(jī)制,將連接上來的客戶端都進(jìn)行注冊(cè),然后不斷循環(huán)掃描各個(gè)客戶端連接,監(jiān)聽客戶端的請(qǐng)求。但是,多路復(fù)用輪詢掃描各個(gè)客戶端連接的過程在操作系統(tǒng)內(nèi)核中進(jìn)行,極大的加快了多路復(fù)用的效率,減少了用戶態(tài)和內(nèi)核態(tài)的切換

2.減少堆內(nèi)內(nèi)存<=>堆外內(nèi)存的拷貝開銷

使用NIO Channel讀寫時(shí)需要需要先讀到堆外內(nèi)存,然后拷貝到堆內(nèi)內(nèi)存,如果直接使用堆外內(nèi)存則可以減少堆外到堆內(nèi)的拷貝過程。

下圖是將Channel數(shù)據(jù)讀取到Buffer,調(diào)用IOUtil#read的源碼

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

下圖是將Buffer數(shù)據(jù)寫入到Channel,調(diào)用IOUtil#write的源碼

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

2.1 為什么需要再堆外內(nèi)存和堆內(nèi)內(nèi)存來回捯飭?

寫入Buffer數(shù)據(jù)到文件描述符,or讀取文件描述符數(shù)據(jù)到Buffer都是需要進(jìn)行系統(tǒng)調(diào)用的,執(zhí)行系統(tǒng)調(diào)用依賴于執(zhí)行native方法,而執(zhí)行native方法的線程被認(rèn)為是處于SafePoint,處于SafePoint就有可能發(fā)生 GC 重排列對(duì)象內(nèi)存的情況。

并且這個(gè)寫入和讀取是針對(duì)地址的(如下圖,最終的native調(diào)用需要傳入地址)如果寫入或者讀取buffer由于gc移動(dòng),那么地址會(huì)改變,但是native方法調(diào)用可不管這個(gè),就導(dǎo)致讀寫出現(xiàn)錯(cuò)誤。因此需要依賴于堆外內(nèi)存。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

2.2 為什么Socket基于Inpustream,OutputStream沒有這個(gè)問題

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

以SokcetInputStream的讀為例,讀最終調(diào)用socktRead0這個(gè)native方法,入?yún)d是當(dāng)前Socket對(duì)應(yīng)的文件描述符,byte數(shù)組就是數(shù)據(jù)最終讀入的目的地。

下圖是native 方法socketRead0的實(shí)現(xiàn)

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

可以看到,其實(shí)是先將socket fd內(nèi)容讀取到c語(yǔ)言聲明的數(shù)組,然后拷貝到Java byte[],這個(gè)c語(yǔ)言聲明的數(shù)組其實(shí)作用類似于直接內(nèi)存!

3.減少內(nèi)核空間和用戶空間的拷貝開銷

上面說了直接內(nèi)存的作用:減少堆外堆內(nèi)的拷貝開銷。無論堆外堆內(nèi),都是用戶空間的拷貝。

3.1 DMA控制器替CPU打工

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

上圖是讀取磁盤文件的時(shí)序圖,可以看到如果沒有DMA技術(shù),藍(lán)色部分需要CPU來完成,將浪費(fèi)寶貴的資源。

再DMA讀取到足夠數(shù)據(jù)后,會(huì)發(fā)送中斷信號(hào)給CPU,讓CPU將內(nèi)核緩沖區(qū)數(shù)據(jù),拷貝到用戶緩沖區(qū),隨后CPU再來調(diào)度Java程序,Java程序才能操作到用戶緩沖區(qū)的數(shù)據(jù)。

3.2 零拷貝

3.2.1 傳統(tǒng)文件傳輸

如下圖是我們使用IO流,讀取磁盤文件,通過Socket API 發(fā)送的流程,其中需要read,和 write 系統(tǒng)調(diào)用,每次系統(tǒng)調(diào)用都意味著用戶態(tài)與內(nèi)核態(tài)的上下文切換。

并且還有四次數(shù)據(jù)拷貝,其中兩次由DMA負(fù)責(zé)打工,兩次由CPU負(fù)責(zé)拷貝。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

如何優(yōu)化:

  • 如果Java程序不需要對(duì)磁盤數(shù)據(jù)內(nèi)容進(jìn)行再加工(業(yè)務(wù)操作)那么不需要拷貝到用戶空間,從而減少拷貝次數(shù)
  • 由于用戶空間沒有操作網(wǎng)卡和磁盤的權(quán)限,操作這些設(shè)備需要由操作系統(tǒng)內(nèi)核完成,那么如果操作系統(tǒng)提供新的系統(tǒng)調(diào)用函數(shù),豈不是就可以減少用戶態(tài)與內(nèi)核態(tài)的上下文切換
3.2.2 mmap + write

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

  • 應(yīng)用進(jìn)程調(diào)用了 mmap() 后,DMA 會(huì)把磁盤的數(shù)據(jù)拷貝到內(nèi)核的緩沖區(qū)里。接著,應(yīng)用進(jìn)程跟操作系統(tǒng)內(nèi)核共享這個(gè)緩沖區(qū);
  • 應(yīng)用進(jìn)程再調(diào)用 write(),操作系統(tǒng)直接將內(nèi)核緩沖區(qū)的數(shù)據(jù)拷貝到 socket 緩沖區(qū)中,這一切都發(fā)生在內(nèi)核態(tài),由 CPU 來搬運(yùn)數(shù)據(jù);
  • 最后,把內(nèi)核的 socket 緩沖區(qū)里的數(shù)據(jù),拷貝到網(wǎng)卡的緩沖區(qū)里,這個(gè)過程是由 DMA 搬運(yùn)的

所以mmap優(yōu)化了什么?

mmap并沒有減少系統(tǒng)調(diào)用帶來的內(nèi)核態(tài)用戶態(tài)切換開銷,只是應(yīng)用程序和內(nèi)核共享緩沖區(qū),從而讓cpu可以直接將內(nèi)核緩沖區(qū)的數(shù)據(jù),拷貝到socket緩沖區(qū),不需要拷貝到用戶緩沖區(qū),再?gòu)挠脩艟彌_區(qū)拷貝到socket緩沖區(qū)。

3.2.3 sendfile

linux 提供sendfile系統(tǒng)調(diào)用,只需這一個(gè)系統(tǒng)調(diào)用就可以從一個(gè)文件描述符拷貝數(shù)據(jù)到另外一個(gè)文件描述符

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

sendfile可以減少write,read導(dǎo)致的系統(tǒng)調(diào)用,從而優(yōu)化效率。

如果網(wǎng)卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技術(shù),那么還可以進(jìn)一步優(yōu)化。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

  1. 通過 DMA 將磁盤上的數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)里;
  2. 緩沖區(qū)描述符和數(shù)據(jù)長(zhǎng)度傳到 socket 緩沖區(qū),這樣網(wǎng)卡的 SG-DMA 控制器就可以直接將內(nèi)核緩存中的數(shù)據(jù)拷貝到網(wǎng)卡的緩沖區(qū)里,此過程不需要將數(shù)據(jù)從操作系統(tǒng)內(nèi)核緩沖區(qū)拷貝到 socket 緩沖區(qū)中,這樣就減少了一次數(shù)據(jù)拷貝。

這便是所謂的零拷貝,減少內(nèi)存層面拷貝數(shù)據(jù)的次數(shù),以及系統(tǒng)調(diào)用內(nèi)核態(tài)用戶態(tài)的切換,從而優(yōu)化性能。

3.3 NIO中的零拷貝

3.3.1 FileChannel#map

NIO中的FileChannel.map()方法使用了mmap系統(tǒng)調(diào)用實(shí)現(xiàn)內(nèi)存映射方式

將內(nèi)核緩沖區(qū)的內(nèi)存和用戶緩沖區(qū)的內(nèi)存做了一個(gè)地址映射。這種方式適合讀取大文件,同時(shí)也能對(duì)文件內(nèi)容進(jìn)行更改,但是如果其后要通過SocketChannel發(fā)送,還是需要CPU進(jìn)行數(shù)據(jù)的拷貝。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

如上是MappedByteBuffer的獲取方式,其實(shí)底層是通過反射調(diào)用DirectByteBuffer的構(gòu)造方法實(shí)現(xiàn)的,其中的cleaner是直接內(nèi)存的回收器,傳入的unmapper會(huì)被回調(diào),從而調(diào)用native方法實(shí)現(xiàn)資源釋放。

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

這種方式適合讀取大文件,同時(shí)也能對(duì)文件內(nèi)容進(jìn)行更改。

3.3.2 FileChannel#transferTo,transerFrom

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

在操作系統(tǒng)層面是調(diào)用的一個(gè)sendFile系統(tǒng)調(diào)用。通過這個(gè)系統(tǒng)調(diào)用,可以在內(nèi)核層直接完成文件內(nèi)容的拷貝。

4.FileChannel#force強(qiáng)制刷盤

由于CPU的運(yùn)行速度非常快,所以CPU在執(zhí)行指令時(shí),通常只能與緩存進(jìn)行交互,而不適合直接操作像磁盤、網(wǎng)卡這樣的硬件。也因此,在進(jìn)行文件寫入時(shí),操作系統(tǒng)也是先寫入到page cache中,緩存起來,然后再往硬件寫入。

緩存有利也有弊,使用page cache頁(yè)緩存,應(yīng)用程序?qū)?shù)據(jù)都寫入到了page cache中,但是卻沒有真正寫入磁盤。如果這個(gè)時(shí)候出現(xiàn)斷電,那么將出現(xiàn)緩存數(shù)據(jù)丟失。

FileChannel#force會(huì)進(jìn)行fsync系統(tǒng)調(diào)用

Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)

fsync可以實(shí)現(xiàn)將page cache緩存內(nèi)容進(jìn)行落盤,從而保證不丟失(redis aof可以設(shè)置持久化機(jī)制,通常設(shè)置每秒落盤一次,這里落盤也是fsync系統(tǒng)調(diào)用)。為了性能考慮,應(yīng)用程序不可能每寫入一點(diǎn)數(shù)據(jù)就調(diào)用fsync,fsync也是有性能損耗的。

四丶IO多路復(fù)用 select/poll/epoll

上面我們聊到了IO多路復(fù)用解決了什么問題,以及NIO Selector的基本使用,但是沒有探究在操作系統(tǒng)層面是如何實(shí)現(xiàn)的,下面來學(xué)習(xí)一下。

1.select系統(tǒng)調(diào)用

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout)
  • nfds: 最大的文件描述符+1,代表監(jiān)聽這一組描述符(為什么要+1?因?yàn)槌水?dāng)前最大描述符之外,還有可能有新的fd連接上來)
  • fd_set: 是一個(gè)位圖集合, 對(duì)于同一個(gè)文件描述符,可以監(jiān)聽不同的事件
  • readfds:文件描述符“可讀”事件
  • writefds:文件描述符“可寫”事件
  • exceptfds:文件描述符“異?!笔录话銉?nèi)核用的,實(shí)際編程很少使用
  • timeout:超時(shí)時(shí)間:0是立即返回,-1是一直阻塞,如果大于0,則達(dá)到設(shè)置值的微秒數(shù)即返回
  • 返回值: 所監(jiān)聽的所有監(jiān)聽集合中滿足條件的總數(shù)(滿足條件的讀、寫、異常事件的總數(shù)),出錯(cuò)時(shí)返回-1,并設(shè)置errno。如果超時(shí)時(shí)間觸發(fā),則返回0

select 其實(shí)就是把NIO中用戶態(tài)要遍歷的fd數(shù)組拷貝到了內(nèi)核態(tài),讓內(nèi)核態(tài)來遍歷,因?yàn)橛脩魬B(tài)判斷socket是否有數(shù)據(jù)依舊需要通過系統(tǒng)調(diào)用,切換到內(nèi)核態(tài)進(jìn)行。

可以看到select依賴了很多位圖參數(shù),系統(tǒng)調(diào)用完后需要用戶程序遍歷一次位圖才能直到哪一個(gè)fd具備了io事件,并且這個(gè)位圖大小最大為1024,導(dǎo)致select用起來需要很多位操作并且最多只能支持1024路IO。

2.poll系統(tǒng)調(diào)用

int poll(struct pollfd *fds, nfds_t nfds/*最大監(jiān)聽的文件描述符個(gè)數(shù)*/, int timeout/*最大監(jiān)聽的文件描述符個(gè)數(shù)*/);

其中pollfd為:

struct pollfd {
      int   fd;         /* file descriptor */
      short events;     /* requested events */
      short revents;    /* returned events */
};

poll可以看作升級(jí)版select,它突破了1024個(gè)文件描述符的限制,并且poll函數(shù)的監(jiān)聽和返回是分開的,簡(jiǎn)化了代碼實(shí)現(xiàn)。

雖然poll不需要遍歷所有的文件描述符了,只需要遍歷加入數(shù)組中的描述符,范圍縮小了很多,但缺點(diǎn)仍然是需要遍歷,當(dāng)加入數(shù)組描述符很多,但是存在事件的fd很少,這個(gè)遍歷操作還是有點(diǎn)不劃算的。

3.epoll系統(tǒng)調(diào)用

在linux環(huán)境下,java nio中的selector就是基于epoll實(shí)現(xiàn)的。

3.1 epoll_create

int epoll_create(int size)
    //返回一個(gè)fd
    //傳入大小作為參考值

epoll_create返回一個(gè)特殊的文件描述符,它代表紅黑樹的根節(jié)點(diǎn)。size則是樹的大小,它代表你將監(jiān)聽多少個(gè)文件描述符。epoll_create將按照傳入的大小,構(gòu)造出一棵大小為size的紅黑樹。

3.2 epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// epfd 是epoll_create的返回值,也就說紅黑樹的根節(jié)點(diǎn)
// op 表示操作,比如增加,修改,刪除
//fd 是需要增加,修改,刪除的文件描述符
// struct epoll_event *event 是一個(gè)結(jié)構(gòu)體,如下
struct epoll_event {
               uint32_t     events;      /* Epoll events 讀事件or寫事件,or 異常事件*/
               epoll_data_t data;        /* User data variable */
           };
 typedef union epoll_data {
               void        *ptr;
               int          fd;//代表一個(gè)文件描述符,初始化的時(shí)候傳入需要監(jiān)聽的文件描述符,當(dāng)監(jiān)聽返回時(shí),此處會(huì)傳出一個(gè)有事件發(fā)生的文件描述符,因此,無需我們遍歷得到結(jié)果了
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

用來操作epoll句柄,可以使用該函數(shù)往紅黑樹里增加文件描述符,修改文件描述符,和刪除文件描述符。

3.3 epoll_wait

int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
//epfd 是epoll_create的返回值,也就說紅黑樹的根節(jié)點(diǎn)
// struct epoll_event *events 是一個(gè)數(shù)組,返回的所有觸發(fā)了事件的文件描述符集合
//maxevents代表這個(gè)數(shù)組的大小
//timeout 0代表立即返回,-1代表永久阻塞,如果大于0,則代表超時(shí)等待毫秒數(shù)

3.4 水平觸發(fā),邊緣觸發(fā)

epoll有兩種觸發(fā)方式,分別為水平觸發(fā)邊沿觸發(fā)。

  • 水平觸發(fā)

    只要有數(shù)據(jù)處于就緒狀態(tài),那么可讀事件就會(huì)一直觸發(fā)。

    舉個(gè)例子,假設(shè)客戶端一次性發(fā)來了4K數(shù)據(jù) ,但是服務(wù)器recv函數(shù)定義的buffer大小僅為1024字節(jié),那么一次肯定是不能將所有數(shù)據(jù)都讀取完的,這時(shí)候就會(huì)繼續(xù)觸發(fā)可讀事件,直到所有數(shù)據(jù)都處理完成。

    epoll默認(rèn)的觸發(fā)方式就是水平觸發(fā)。

  • 邊緣觸發(fā)

    只有數(shù)據(jù)發(fā)送過來的時(shí)候會(huì)觸發(fā)一次,即使數(shù)據(jù)沒有讀取完,也不會(huì)繼續(xù)觸發(fā)。

  • 觸發(fā)方式的設(shè)置:

    水平觸發(fā)和邊沿觸發(fā)在內(nèi)核里 使用兩個(gè)bit mask區(qū)分,分別為:

    • EPOLLLT 水平 觸發(fā)
    • EPOLLET 邊沿觸發(fā)

    需要在注冊(cè)事件的時(shí)候?qū)⑵渑c需要注冊(cè)的事件做一個(gè)位或運(yùn)算即可:

    ev.events = EPOLLIN;    //LT
    ev.events = EPOLLIN | EPOLLET;   //ET
    

4.總結(jié)

select函數(shù)需要一次性傳入所有需要監(jiān)控的連接(在內(nèi)核中是FD),并在內(nèi)核中對(duì)這些FD進(jìn)行持續(xù)的掃描。當(dāng)發(fā)現(xiàn)其中有FD不老實(shí)時(shí),就會(huì)通知應(yīng)用程序有客戶端事件發(fā)生了, 上層應(yīng)用接到通知后,就只能自己再去遍歷所有的FD,尋找有事件發(fā)生的連接,然后進(jìn)行業(yè)務(wù)處理。
但是select受限于操作系統(tǒng),掃描的FD個(gè)數(shù)是受限的。

于是出現(xiàn)了Poll函數(shù),解決了slelect文件描述符受限的問題。但是,上層應(yīng)用程序依然要自己去遍歷所有客戶端,尋找哪個(gè)客戶端上有事件發(fā) 生。高并發(fā)場(chǎng)景下,性能依然嚴(yán)重受限。
于是又出現(xiàn)了epoll機(jī)制。

epoll機(jī)制會(huì)直接返回有事件發(fā)生的FD。這樣就省掉了上層應(yīng)用頻繁掃描所有客戶端的消耗,進(jìn)一步解決多路復(fù)用的高并發(fā)問題。文章來源地址http://www.zghlxwxcb.cn/news/detail-479135.html

到了這里,關(guān)于Java NIO原理 (Selector、Channel、Buffer、零拷貝、IO多路復(fù)用)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Java NIO Selector選擇器源碼分析

    Java NIO Selector選擇器源碼分析

    Java NIO(New I/O)的Selector選擇器是一個(gè)用于多路復(fù)用(Multiplexing)的I/O操作的關(guān)鍵組件。它允許一個(gè)單獨(dú)的線程監(jiān)視多個(gè)通道(Channel)的可讀性和可寫性,從而有效地管理大量并發(fā)連接。 AbstractSelector主要實(shí)現(xiàn)了Selector的打開關(guān)閉的狀態(tài)維護(hù),支持異步關(guān)閉和中斷的begin和end方

    2024年04月10日
    瀏覽(17)
  • Java NIO (三)NIO Channel類

    Java NIO (三)NIO Channel類

    ? ? ? ? 前面提到,Java NIO中一個(gè)socket連接使用一個(gè)Channel來表示。從更廣泛的層面來說,一個(gè)通道可以表示一個(gè)底層的文件描述符,例如硬件設(shè)備、文件、網(wǎng)絡(luò)連接等。然而,遠(yuǎn)不止如此,Java NIO的通道可以更加細(xì)化。例如,不同的網(wǎng)絡(luò)傳輸協(xié)議,在Java中都有不同的NIO Chann

    2024年01月18日
    瀏覽(47)
  • Java-NIO篇章(4)——Selector選擇器詳解

    Java-NIO篇章(4)——Selector選擇器詳解

    選擇器(Selector)是什么呢?選擇器和通道的關(guān)系又是什么?這里詳細(xì)說明,假設(shè)不用選擇器,那么一個(gè)客戶端請(qǐng)求數(shù)據(jù)傳輸那就需要建立一個(gè)連接,為了避免線程阻塞,那么每個(gè)客戶端開辟一個(gè)線程。而學(xué)過JVM的都知道,默認(rèn)每開一個(gè)線程需要??臻g內(nèi)存1MB大小。如果這時(shí)

    2024年01月21日
    瀏覽(31)
  • Java-NIO篇章(3)——Channel通道類詳解

    Java NIO中,一個(gè)socket連接使用一個(gè)Channel(通道)來表示。對(duì)應(yīng)到不同的網(wǎng)絡(luò)傳輸協(xié)議類型,在Java中都有不同的NIO Channel(通道) 相對(duì)應(yīng)。其中最為重要的四種Channel(通道)實(shí)現(xiàn): FileChannel、 SocketChannel、 ServerSocketChannel、 DatagramChannel : FileChannel 文件通道,用于文件的數(shù)據(jù)讀

    2024年01月20日
    瀏覽(24)
  • java nio零拷貝

    java nio零拷貝

    ? 零拷貝是一種計(jì)算機(jī)執(zhí)行IO操作的優(yōu)化技術(shù),其核心目標(biāo)是減少數(shù)據(jù)拷貝次數(shù),從而提高系統(tǒng)性能。它主要體現(xiàn)在以下幾個(gè)方面: 1. **定義與原理**:零拷貝字面上的意思包括“零”和“拷貝”。其中,“拷貝”是指數(shù)據(jù)從一個(gè)存儲(chǔ)區(qū)域轉(zhuǎn)移到另一個(gè)存儲(chǔ)區(qū)域;“零”表示次

    2024年02月20日
    瀏覽(18)
  • 從Java BIO到NIO再到多路復(fù)用,看這篇就夠了

    從Java BIO到NIO再到多路復(fù)用,看這篇就夠了

    目錄 從一次優(yōu)化說起 IO模型分類 分類 舉例 概念詳解 阻塞和非阻塞 同步與異步 Java支持版本 實(shí)戰(zhàn) c10k問題 上代碼 BIO服務(wù)端 NIO服務(wù)端??????? 多路復(fù)用 概念 階段一:selectpoll 階段二epoll Java selector 后記 ????????近期優(yōu)化了一個(gè)老的網(wǎng)關(guān)系統(tǒng),在dubbo調(diào)用接口rt100

    2024年02月08日
    瀏覽(18)
  • Java 中的 IO 和 NIO

    Java IO(Input/Output)流是用于處理輸入和輸出數(shù)據(jù)的機(jī)制。它提供了一種標(biāo)準(zhǔn)化的方式來讀取和寫入數(shù)據(jù),可以與文件、網(wǎng)絡(luò)、標(biāo)準(zhǔn)輸入輸出等進(jìn)行交互。 Java IO流主要分為兩個(gè)流模型:字節(jié)流(Byte Stream)和字符流(Character Stream)。 字節(jié)流(Byte Stream) InputStream:字節(jié)輸入流

    2024年02月10日
    瀏覽(23)
  • java-IO&NIO

    java-IO&NIO

    1.1. 阻塞 IO 模型 ??最傳統(tǒng)的一種 IO 模型,即在讀寫數(shù)據(jù)過程中會(huì)發(fā)生阻塞現(xiàn)象。當(dāng)用戶線程發(fā)出 IO 請(qǐng)求之后,內(nèi) 核會(huì)去查看數(shù)據(jù)是否就緒,如果沒有就緒就會(huì)等待數(shù)據(jù)就緒,而用戶線程就會(huì)處于阻塞狀態(tài),用戶線程交出 CPU。當(dāng)數(shù)據(jù)就緒之后,內(nèi)核會(huì)將數(shù)據(jù)拷貝到用戶線程

    2024年02月12日
    瀏覽(21)
  • NIO核心三:Selector

    NIO核心三:Selector

    選擇器提供一種選擇執(zhí)行已經(jīng)就緒的任務(wù)的能力。selector選擇器可以讓單線程處理多個(gè)通道。如果程序打開了多個(gè)連接通道,每個(gè)連接的流量都比較低,可以使用Selector對(duì)通道進(jìn)行管理。 1.創(chuàng)建Selector 2.必須將通道設(shè)置為非阻塞模式才能注冊(cè)到選擇器上 3.把通道注冊(cè)到選擇器上

    2024年03月11日
    瀏覽(22)
  • Java NIO(Java Non-Blocking IO:非阻塞式IO)(2)

    Java NIO(Java Non-Blocking IO:非阻塞式IO)(2)

    1.NIO非阻塞網(wǎng)絡(luò)編程相關(guān)的(Selector、SelectionKey、ServerScoketChannel和SocketChannel)關(guān)系梳理圖: 說明: ①.當(dāng)客戶端連接時(shí),會(huì)通過服務(wù)器端ServerSocketChannel得到/生成對(duì)應(yīng)的SocketChannel; ②.通過register(Selector sel,int ops)方法將SocketChannel注冊(cè)到Selector上(一個(gè)Selector上可以注冊(cè)多個(gè)SocketChannel); ③

    2024年02月02日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包