摘要:上篇說了最基礎(chǔ)的五種模型,相信大家對(duì)相關(guān)的概念應(yīng)該有了一定的了解,這篇文章主要講講基于多路復(fù)用的。
上篇說了最基礎(chǔ)的五種IO模型,相信大家對(duì)IO相關(guān)的概念應(yīng)該有了一定的了解,這篇文章主要講講基于多路復(fù)用IO的Java NIO。
背景Java誕生至今,有好多種IO模型,從最早的Java IO到后來的Java NIO以及最新的Java AIO,每種IO模型都有它自己的特點(diǎn),詳情請(qǐng)看我的上篇文章[Java IO初探](),而其中的的Java NIO應(yīng)用非常廣泛,尤其是在高并發(fā)領(lǐng)域,比如我們常見的Netty,Mina等框架,都是基于它實(shí)現(xiàn)的,相信大家都有所了解,下面讓我們來看看Java NIO的具體架構(gòu)。
Java NIO架構(gòu)其實(shí)Java NIO模型相對(duì)來說也還是比較簡(jiǎn)單的,它的核心主要有三個(gè),分別是:Selector、Channel和Buffer,我們先來看看它們之間的關(guān)系:
它們之間的關(guān)系很清晰,一個(gè)線程對(duì)應(yīng)著一個(gè)Selector,一個(gè)Selector對(duì)應(yīng)著多個(gè)Channel,一個(gè)Channel對(duì)應(yīng)著一個(gè)Buffer,當(dāng)然這只是通常的做法,一個(gè)Channel也可以對(duì)應(yīng)多個(gè)Selector,一個(gè)Channel對(duì)應(yīng)著多個(gè)Buffer。
Selector個(gè)人認(rèn)為Selector是Java NIO的最大特點(diǎn),之前我們說過,傳統(tǒng)的Java IO在面對(duì)大量IO請(qǐng)求的時(shí)候有心無力,因?yàn)槊總€(gè)維護(hù)每一個(gè)IO請(qǐng)求都需要一個(gè)線程,這帶來的問題就是,系統(tǒng)資源被極度消耗,吞吐量直線下降,引起系統(tǒng)相關(guān)問題,那么Java NIO是如何解決這個(gè)問題的呢?答案就是Selector,簡(jiǎn)單來說它對(duì)應(yīng)著多路IO復(fù)用中的監(jiān)管角色,它負(fù)責(zé)統(tǒng)一管理IO請(qǐng)求,監(jiān)聽相應(yīng)的IO事件,并通知對(duì)應(yīng)的線程進(jìn)行處理,這種模式下就無需為每個(gè)IO請(qǐng)求多帶帶分配一個(gè)線程,另外也減少線程大量阻塞,資源利用率下降的情況,所以說Selector是Java NIO的精髓,在Java中我們可以這么寫:
// 打開服務(wù)器套接字通道 ServerSocketChannel ssc = ServerSocketChannel.open(); // 服務(wù)器配置為非阻塞 ssc.configureBlocking(false); // 進(jìn)行服務(wù)的綁定 ssc.bind(new InetSocketAddress("localhost", 8001)); // 通過open()方法找到Selector Selector selector = Selector.open(); // 注冊(cè)到selector,等待連接 ssc.register(selector, SelectionKey.OP_ACCEPT); ...Channel
Channel本意是通道的意思,簡(jiǎn)單來說,它在Java NIO中表現(xiàn)的就是一個(gè)數(shù)據(jù)通道,但是這個(gè)通道有一個(gè)特點(diǎn),那就是它是雙向的,也就是說,我們可以從通道里接收數(shù)據(jù),也可以向通道里寫數(shù)據(jù),不用像Java BIO那樣,讀數(shù)據(jù)和寫數(shù)據(jù)需要不同的數(shù)據(jù)通道,比如最常見的Inputstream和Outputstream,但是它們都是單向的,Channel作為一種全新的設(shè)計(jì),它幫助系統(tǒng)以相對(duì)小的代價(jià)來保持IO請(qǐng)求數(shù)據(jù)傳輸?shù)奶幚恚撬⒉徽嬲娣艛?shù)據(jù),它總是結(jié)合著緩存區(qū)(Buffer)一起使用,另外Channel主要有以下四種:
FileChannel:讀寫文件時(shí)使用的通道
DatagramChannel:傳輸U(kuò)DP連接數(shù)據(jù)時(shí)的通道,與Java IO中的DatagramSocket對(duì)應(yīng)
SocketChannel:傳輸TCP連接數(shù)據(jù)時(shí)的通道,與Java IO中的Socket對(duì)應(yīng)
ServerSocketChannel: 監(jiān)聽套接詞連接時(shí)的通道,與Java IO中的ServerSocket對(duì)應(yīng)
當(dāng)然其中最重要以及最常用的就是SocketChannel和ServerSocketChannel,也是Java NIO的精髓,ServerSocketChannel可以設(shè)置成非阻塞模式,然后結(jié)合Selector就可以實(shí)現(xiàn)多路復(fù)用IO,使用一個(gè)線程管理多個(gè)Socket連接,具體使用可以參數(shù)上面的代碼。
Buffer顧名思義,Buffer的含義是緩沖區(qū),它在Java NIO中的主要作用就是作為數(shù)據(jù)的緩沖區(qū)域,Buffer對(duì)應(yīng)著某一個(gè)Channel,從Channel中讀取數(shù)據(jù)或者向Channel中寫數(shù)據(jù),Buffer與數(shù)組很類似,但是它提供了更多的特性,方便我們對(duì)Buffer中的數(shù)據(jù)進(jìn)行操作,后面我也會(huì)主要分析它的三個(gè)屬性capacity,position和limit,我們先來看一下Buffer分配時(shí)的類別(這里不是指Buffer的具體數(shù)據(jù)類型)即Direct Buffer和Heap Buffer,那么為什么要有這兩種類別的Buffer呢?我們先來看看它們的特性:
Direct Buffer:
直接分配在系統(tǒng)內(nèi)存中;
不需要花費(fèi)將數(shù)據(jù)庫從內(nèi)存拷貝到Java內(nèi)存中的成本;
雖然Direct Buffer是直接分配中系統(tǒng)內(nèi)存中的,但當(dāng)它被重復(fù)利用時(shí),只有真正需要數(shù)據(jù)的那一頁數(shù)據(jù)會(huì)被裝載到真是的內(nèi)存中,其它的還存在在虛擬內(nèi)存中,不會(huì)造成實(shí)際內(nèi)存的資源浪費(fèi);
可以結(jié)合特定的機(jī)器碼,一次可以有順序的讀取多字節(jié)單元;
因?yàn)橹苯臃峙湓谙到y(tǒng)內(nèi)存中,所以它不受Java GC管理,不會(huì)自動(dòng)回收;
創(chuàng)建以及銷毀的成本比較高;
Heap Buffer:
分配在Java Heap,受Java GC管理生命周期,不需要額外維護(hù);
創(chuàng)建成本相對(duì)較低;
根據(jù)它們的特性,我們可以大致總結(jié)出它們的適用場(chǎng)景:
如果這個(gè)Buffer可以重復(fù)利用,而且你也想多個(gè)字節(jié)操作,亦或者你對(duì)性能要求很高,可以選擇使用Direct Buffer,但其編碼相對(duì)來說會(huì)比較復(fù)雜,需要注意的點(diǎn)也更多,反之則用Heap Buffer,Buffer的相應(yīng)創(chuàng)建方法:
//創(chuàng)建Heap Buffer ByteBuffer heapBuffer = ByteBuffer.allocate(1024); //創(chuàng)建Direct Buffer ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
下面我們來看看它的三個(gè)屬性:
Capacity:顧名思義它的含義是容量,代表著Buffer的最大容量,與數(shù)組的Size很類似,初始化不可更改,除非你改變的Buffer的結(jié)構(gòu);
Limit:顧名思義它的含義是界限,代表著Buffer的目前可使用的最大限制,寫模式下,一般Limit等于Capacity,讀模式下需要你自己控制它的值結(jié)合position讀取想要的數(shù)據(jù);
Position:顧名思義它的含義是位置,代表著Buffer目前操作的位置,通俗來說,就是你下次對(duì)Buffer進(jìn)行操作的起始位置;
接下來我會(huì)用一個(gè)圖解的列子幫助大家理解,現(xiàn)在我們假設(shè)有一個(gè)容量為10的Buffer,我們先往里面寫入一定字節(jié)的數(shù)據(jù),然后再根據(jù)編碼規(guī)則從其中讀取我們需要的數(shù)據(jù):
1.初始Buffer:
ByteBuffer buffer = ByteBuffer.allocate(10);
2.向Buffer中寫入兩個(gè)字節(jié):
buffer.put("my".getBytes());
3.再Buffer中寫入四個(gè)字節(jié):
buffer.put("blog".getBytes());
4.現(xiàn)在我們需要從Buffer中獲取數(shù)據(jù),首先我們先將寫模式轉(zhuǎn)換為讀模式:
buffer.flip();
我們來看看flip()方法到底做了什么事?
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
從源碼中可以看出,flip方法根據(jù)Buffer目前的相應(yīng)屬性來修改對(duì)應(yīng)的屬性,所以flip()方法之后,Buffer目前的狀態(tài):
5.接著我們從Buffer中讀取數(shù)據(jù)
從Buffer中讀取數(shù)據(jù)有多種方式,比如get(),get(byte [])等,相關(guān)的具體方法使用可以參考Buffer的官方API文檔,這里我們用最簡(jiǎn)單的get()來獲取數(shù)據(jù):
byte a = buffer.get(); byte b = buffer.get();
此時(shí)Buffer的狀態(tài)如下圖所示:
我們可以按照這種方式讀取完我們所需數(shù)據(jù),最終調(diào)用clear()方法將Buffer置為初始狀態(tài)。
總結(jié)這篇文章主要講解了Java NIO中重要的三個(gè)組成部分,在實(shí)際使用過程也是比較重要的,掌握它們之間的關(guān)系,可以讓你對(duì)Java NIO的整個(gè)架構(gòu)更加熟悉,理解相對(duì)來說也會(huì)更加深刻,并分析了這種模式是如何與多路復(fù)用IO模型的映射,了解Java NIO在高并發(fā)場(chǎng)景下優(yōu)勢(shì)的原因。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70632.html
摘要:從通道進(jìn)行數(shù)據(jù)寫入創(chuàng)建一個(gè)緩沖區(qū),填充數(shù)據(jù),并要求通道寫入數(shù)據(jù)。三之通道主要內(nèi)容通道介紹通常來說中的所有都是從通道開始的。從中選擇選擇器維護(hù)注冊(cè)過的通道的集合,并且這種注冊(cè)關(guān)系都被封裝在當(dāng)中停止選擇的方法方法和方法。 由于內(nèi)容比較多,我下面放的一部分是我更新在我的微信公眾號(hào)上的鏈接,微信排版比較好看,更加利于閱讀。每一篇文章下面我都把文章的主要內(nèi)容給列出來了,便于大家學(xué)習(xí)與回顧。 Ja...
摘要:線程之間的切換對(duì)于操作系統(tǒng)來說是昂貴的。因此,單線程可以監(jiān)視多個(gè)通道中的數(shù)據(jù)。當(dāng)方法返回后,線程可以處理這些事件。 一 NIO簡(jiǎn)介 Java NIO 是 java 1.4 之后新出的一套IO接口,這里的的新是相對(duì)于原有標(biāo)準(zhǔn)的Java IO和Java Networking接口。NIO提供了一種完全不同的操作方式。 NIO中的N可以理解為Non-blocking,不單純是New。 它支持面...
摘要:學(xué)習(xí)和掌握技術(shù)已經(jīng)不是一個(gè)攻城獅的加分技能,而是一個(gè)必備技能。是雙向的,不僅可以讀取數(shù)據(jù)還能保存數(shù)據(jù),程序不能直接讀寫通道,只與緩沖區(qū)交互為了讓大家不被高并發(fā)與大量連接處理問題所困擾,動(dòng)力節(jié)點(diǎn)推出了高效處理模型應(yīng)用教程。 大家肯定了解Java IO, 但是對(duì)于NIO一般是陌生的,而現(xiàn)在使用到NIO的場(chǎng)景越來越多,很多技術(shù)框...
摘要:目錄源碼分析之番外篇的前生今世的前生今世之一簡(jiǎn)介的前生今世之二小結(jié)的前生今世之三詳解的前生今世之四詳解源碼分析之零磨刀不誤砍柴工源碼分析環(huán)境搭建源碼分析之一揭開神秘的紅蓋頭源碼分析之一揭開神秘的紅蓋頭客戶端源碼分析之一揭開神秘的紅蓋頭服務(wù)器 目錄 Netty 源碼分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 簡(jiǎn)介 Java NIO 的前生今世 ...
摘要:通道可以異步讀寫。使用的方法讀取數(shù)據(jù)創(chuàng)建一個(gè)讀數(shù)據(jù)緩沖區(qū)對(duì)象從通道中讀取數(shù)據(jù)使用的方法寫入數(shù)據(jù)創(chuàng)建一個(gè)寫數(shù)據(jù)緩沖區(qū)對(duì)象寫入數(shù)據(jù)關(guān)閉完成使用后,您必須關(guān)閉它。五提供了一種被稱為的新功能,也稱為本地矢量。功能是通道提供的并不是。 歷史回顧: Java NIO 概覽 Java NIO 之 Buffer(緩沖區(qū)) 其他高贊文章: 面試中關(guān)于Redis的問題看這篇就夠了 一文輕松搞懂redis集...
閱讀 2254·2021-09-26 09:55
閱讀 3584·2021-09-23 11:22
閱讀 2151·2019-08-30 15:54
閱讀 1894·2019-08-28 18:03
閱讀 2593·2019-08-26 12:22
閱讀 3426·2019-08-26 12:20
閱讀 1723·2019-08-26 11:56
閱讀 2245·2019-08-23 15:30