Java IO之NIO

上篇說了最基礎(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)系:

java-nio

它們之間的關(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)求單獨(dú)分配一個(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);
init-buffer.png

2.向Buffer中寫入兩個(gè)字節(jié):

buffer.put("my".getBytes());
write-buffer-1.png

3.再Buffer中寫入四個(gè)字節(jié):

buffer.put("blog".getBytes());
write-buffer-2.png

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):

read-buffer.png

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)如下圖所示:

read-buffer-2.png

我們可以按照這種方式讀取完我們所需數(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)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痊夭,一起剝皮案震驚了整個(gè)濱河市刁岸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌她我,老刑警劉巖虹曙,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異番舆,居然都是意外死亡酝碳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門恨狈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疏哗,“玉大人,你說我怎么就攤上這事禾怠》捣睿” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵吗氏,是天一觀的道長(zhǎng)衡瓶。 經(jīng)常有香客問我,道長(zhǎng)牲证,這世上最難降的妖魔是什么哮针? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上十厢,老公的妹妹穿的比我還像新娘等太。我一直安慰自己,他們只是感情好蛮放,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布缩抡。 她就那樣靜靜地躺著,像睡著了一般包颁。 火紅的嫁衣襯著肌膚如雪瞻想。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天娩嚼,我揣著相機(jī)與錄音蘑险,去河邊找鬼。 笑死岳悟,一個(gè)胖子當(dāng)著我的面吹牛佃迄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贵少,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呵俏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了滔灶?” 一聲冷哼從身側(cè)響起普碎,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎录平,沒想到半個(gè)月后麻车,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萄涯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年绪氛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唆鸡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涝影。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖争占,靈堂內(nèi)的尸體忽然破棺而出燃逻,到底是詐尸還是另有隱情,我是刑警寧澤臂痕,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布伯襟,位于F島的核電站,受9級(jí)特大地震影響握童,放射性物質(zhì)發(fā)生泄漏姆怪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稽揭。 院中可真熱鬧俺附,春花似錦、人聲如沸溪掀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽揪胃。三九已至璃哟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喊递,已是汗流浹背随闪。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留册舞,地道東北人蕴掏。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像调鲸,于是被迫代替她去往敵國和親盛杰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Java NIO(New IO)是從Java 1.4版本開始引入的一個(gè)新的IO API藐石,可以替代標(biāo)準(zhǔn)的Java I...
    JackChen1024閱讀 7,555評(píng)論 1 143
  • 簡(jiǎn)介 Java NIO 是由 Java 1.4 引進(jìn)的異步 IO.Java NIO 由以下幾個(gè)核心部分組成: Ch...
    永順閱讀 1,791評(píng)論 0 15
  • 這兩天了解了一下關(guān)于NIO方面的知識(shí)即供,網(wǎng)上關(guān)于這一塊的介紹只是介紹了一下基本用法,沒有系統(tǒng)的解釋NIO與阻塞于微、非阻...
    Ruheng閱讀 7,124評(píng)論 5 48
  • (轉(zhuǎn)載說明:本文非原創(chuàng)逗嫡,轉(zhuǎn)載自http://ifeve.com/java-nio-all/) Java NIO: ...
    數(shù)獨(dú)題閱讀 805評(píng)論 0 3
  • (轉(zhuǎn)載說明:本文非原創(chuàng)驱证,轉(zhuǎn)載自http://ifeve.com/java-nio-all/) Java NIO: ...
    柳岸閱讀 818評(píng)論 0 3