Java nio都是非阻塞IO么睬辐?并非如此

java中的nio包,對(duì)于java程序員來(lái)說(shuō)是個(gè)熟悉又陌生的東西症汹。以前一直以為nio=Non-blocking I/O硫朦,即非阻塞IO。后來(lái)又聽(tīng)人說(shuō)nio其實(shí)是new IO新一代IO的意思背镇。兩種說(shuō)法到底哪種是正確的咬展?我去Oracle的java官網(wǎng)查看doc,很遺憾也沒(méi)直接解釋nio是什么單詞的縮寫瞒斩。但是經(jīng)過(guò)一番實(shí)踐破婆,確證new IO才是nio正確的全稱。

一段代碼引發(fā)的思考

    ByteBuffer buf = ByteBuffer.allocate(48);
    buf.clear();

    channelA.read(buf);

    buf.flip();

    while(buf.hasRemaining()) {
        channelB.write(buf);
    }

這是在網(wǎng)上搜FileChannel看到的一段代碼胸囱。這個(gè)時(shí)候我以為nio下的一切io操作都是非阻塞的祷舀,于是看到這段代碼我就非常費(fèi)解。
為什么FileChannel在read的時(shí)候沒(méi)有加while判斷,在write的時(shí)候就要加while判斷了裳扯?難道說(shuō)對(duì)FileChannel來(lái)說(shuō)read是線程阻塞操作抛丽,write是非阻塞的?這就有點(diǎn)匪夷所思了饰豺∫谙剩看到doc文檔也沒(méi)找到答案。

于是我自己試驗(yàn)了一下哟忍,在while循環(huán)里添加日志狡门,如果write是非阻塞的,那么while應(yīng)該打印很多次锅很。最后的結(jié)果是while里只打印一次日志其馏,代表read和write都是阻塞的!
這時(shí)候其實(shí)我更迷惑了爆安,因?yàn)槲乙恢币詾閚io下的io操作都是非阻塞的叛复。
于是再次去百度FileChannel到底是阻塞還是非阻塞的。找了很久沒(méi)找到確切的答案扔仓,最后在StackOverflow上找到了一個(gè)比較滿意的回答:

UNIX does not support non-blocking I/O for files, see Non-blocking I/O with regular files. As Java should (at least try to) provide the same behaviour on all platforms, the FileChannel does not implement SelectableChannel.
UNIX不支持文件的非阻塞IO褐奥,參看這個(gè)網(wǎng)頁(yè)的說(shuō)明。由于Java在全平臺(tái)上要保持行為一致(或者努力這么做)翘簇,所以FileChannel沒(méi)有實(shí)現(xiàn)SelectableChannel撬码。

However Java 7 will include a new AsynchronousFileChannel class that supports asynchronous file I/O, which is a different mechanism to non-blocking I/O.
但是Java7上引入了一個(gè)支持異步文件IO的AsynchronousFileChannel類,但是這是跟非阻塞IO不一樣的機(jī)制版保。

In general only sockets and pipes truly support non-blocking I/O via select() mechanism.
一般來(lái)說(shuō)只有socktes和pipes通過(guò)select機(jī)制真正支持非阻塞IO呜笑。

從這段話來(lái)說(shuō),首先FileChannel肯定是阻塞IO的彻犁;其次實(shí)現(xiàn)了SelectableChannel接口才能實(shí)現(xiàn)非阻塞IO叫胁。從FileChannel上也能看出來(lái),nio下不是所有io操作都是非阻塞的汞幢。因此nio的全稱絕不應(yīng)該是non-blocking I/O驼鹅,而應(yīng)該是new IO。
當(dāng)然這里還說(shuō)了異步和非阻塞是完全不一樣的機(jī)制森篷,這個(gè)以后再來(lái)了解吧输钩。

Channels&Buffers

nio下有很多類,對(duì)我們來(lái)說(shuō)以下三個(gè)是最重要的:

  • Channels
  • Buffers
  • Selectors
    第一代io使用字節(jié)流和字符流來(lái)進(jìn)行讀寫疾宏,而nio使用Channels和Buffers來(lái)實(shí)現(xiàn)讀寫张足。數(shù)據(jù)總是從Channel讀到buffer里,再?gòu)腷uffer寫到Channel里坎藐。這個(gè)是使用上很大的區(qū)別。
    Channel和字符字節(jié)流有點(diǎn)類似,但是Channel都是雙向的岩馍,既可以讀碉咆,也可以寫。
    以前的io流我們一般直接定義一個(gè)byte數(shù)組來(lái)作為buffer蛀恩,但是nio里需要使用Buffer類疫铜。
    下面是網(wǎng)上找的一個(gè)使用FileChannel和Buffer的例子:
    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
    FileChannel inChannel = aFile.getChannel();

    //創(chuàng)建48個(gè)字節(jié)長(zhǎng)度的ByteBuffer
    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = inChannel.read(buf); //從Channel中讀取數(shù)據(jù)到buffer里
    while (bytesRead != -1) {

    buf.flip();  //切換buffer到讀取狀態(tài)

    while(buf.hasRemaining()){
        System.out.print((char) buf.get()); //每次讀取一個(gè)字節(jié)
    }

    buf.clear(); //清空buffer后才能繼續(xù)從Channel里讀取數(shù)據(jù)
    bytesRead = inChannel.read(buf);
    
    aFile.close();

Buffer實(shí)例化的時(shí)候需要傳入長(zhǎng)度。在buffer寫好數(shù)據(jù)双谆,準(zhǔn)備讀取到Channel里的時(shí)候壳咕,一定要執(zhí)行flip操作,buffer才可以讀取顽馋。同樣的谓厘,再次寫入數(shù)據(jù)之前,buffer需要clear一下清除數(shù)據(jù)寸谜。

Selector和非阻塞IO

但是nio最吸引我們的肯定還是非阻塞IO竟稳,而java nio里非阻塞IO的實(shí)現(xiàn)要靠Selector。Selector是一種IO多路復(fù)用機(jī)制的實(shí)現(xiàn)熊痴。簡(jiǎn)單來(lái)說(shuō)他爸,就是用單個(gè)線程/進(jìn)程來(lái)對(duì)多個(gè)IO Channel進(jìn)行輪詢。內(nèi)核不管IO有沒(méi)有完成都會(huì)立即返回給用戶果善,這樣單個(gè)IO的阻塞就不會(huì)阻塞用戶線程诊笤。單個(gè)線程無(wú)需一個(gè)個(gè)等待IO完成再工作,而是發(fā)現(xiàn)有完成的就處理巾陕,處理完繼續(xù)輪詢讨跟,直到下一個(gè)完成的IO出現(xiàn)。

這樣做的好處就是單個(gè)線程即可處理海量并發(fā)請(qǐng)求惜论,當(dāng)然前提是業(yè)務(wù)的數(shù)據(jù)處理不能太耗時(shí)许赃,否則線程會(huì)卡在數(shù)據(jù)處理上。NodeJs就是單線程非阻塞IO模型馆类,因此擅長(zhǎng)處理高并發(fā)混聊。同樣適合非阻塞IO的還有nginx、redis等高并發(fā)但是計(jì)算簡(jiǎn)單的軟件乾巧。而Java數(shù)據(jù)庫(kù)IO連接的JDBC是阻塞io句喜,因此Java服務(wù)器不太適合nio,而適合多線程來(lái)處理并發(fā)沟于。

對(duì)Android程序員來(lái)說(shuō)咳胃,Looper中的MessgeQueue在執(zhí)行next方法獲取下一條handler消息的時(shí)候,如果沒(méi)有消息旷太,主線程執(zhí)行nativePollOnce方法阻塞住展懈。這樣主線程在沒(méi)事的時(shí)候就不會(huì)消耗cpu資源销睁。在有消息傳過(guò)來(lái)時(shí),android framework除了把消息添加到MessageQueue存崖,還會(huì)把主線程給wakeup冻记。那么怎么樣block(阻塞)線程和wakeup(喚醒)線程呢?通過(guò)linux系統(tǒng)的epoll機(jī)制来惧,而epoll機(jī)制就是selector相對(duì)的poll機(jī)制的加強(qiáng)版冗栗。

以上只是我的一點(diǎn)形而上的理解,有可能存在謬誤供搀,推薦幾個(gè)解釋的好的博文來(lái)學(xué)習(xí)Selector以及IO多路復(fù)用的概念:

Java NIO 系列教程(翻譯的國(guó)外教程)

聊聊Linux 五種IO模型

StackOverflow上關(guān)于NativePollOnce方法的討論

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末隅居,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子葛虐,更是在濱河造成了極大的恐慌胎源,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挡闰,死亡現(xiàn)場(chǎng)離奇詭異乒融,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)摄悯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門赞季,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人奢驯,你說(shuō)我怎么就攤上這事申钩。” “怎么了瘪阁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵撒遣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我管跺,道長(zhǎng)义黎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任豁跑,我火速辦了婚禮廉涕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艇拍。我一直安慰自己狐蜕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布卸夕。 她就那樣靜靜地躺著层释,像睡著了一般。 火紅的嫁衣襯著肌膚如雪快集。 梳的紋絲不亂的頭發(fā)上贡羔,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天廉白,我揣著相機(jī)與錄音,去河邊找鬼治力。 笑死蒙秒,一個(gè)胖子當(dāng)著我的面吹牛勃黍,可吹牛的內(nèi)容都是我干的宵统。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼覆获,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼马澈!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起弄息,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痊班,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后摹量,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體涤伐,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年缨称,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凝果。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睦尽,死狀恐怖器净,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情当凡,我是刑警寧澤山害,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站沿量,受9級(jí)特大地震影響浪慌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜朴则,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一权纤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧佛掖,春花似錦妖碉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拴魄,卻和暖如春冗茸,著一層夾襖步出監(jiān)牢的瞬間席镀,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工夏漱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豪诲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓挂绰,卻偏偏與公主長(zhǎng)得像屎篱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子葵蒂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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

  • 本系列文章將整理到我在GitHub上的《Java面試指南》倉(cāng)庫(kù)交播,更多精彩內(nèi)容請(qǐng)到我的倉(cāng)庫(kù)里查看 https://g...
    da3acf50377b閱讀 397評(píng)論 0 1
  • 這兩天了解了一下關(guān)于NIO方面的知識(shí),網(wǎng)上關(guān)于這一塊的介紹只是介紹了一下基本用法践付,沒(méi)有系統(tǒng)的解釋NIO與阻塞秦士、非阻...
    Ruheng閱讀 7,129評(píng)論 5 48
  • 同步和異步、阻塞和非阻塞 同步 (synchronous) 是一種可靠的運(yùn)行機(jī)制永高,當(dāng)我們進(jìn)行同步操作時(shí)隧土,后續(xù)操作是...
    wean_a23e閱讀 210評(píng)論 0 0
  • IO流學(xué)習(xí)總結(jié) 一Java IO,硬骨頭也能變軟 (1) 按操作方式分類結(jié)構(gòu)圖: (2)按操作對(duì)象分類結(jié)構(gòu)圖 二j...
    綠葉悠閱讀 620評(píng)論 0 0
  • (1) 按操作方式分類結(jié)構(gòu)圖: (2)按操作對(duì)象分類結(jié)構(gòu)圖 二java IO體系的學(xué)習(xí)總結(jié) IO流的分類:按照流的...
    JavaEdge閱讀 540評(píng)論 0 13