Java nio深入淺出

一直有著對(duì)nio的高效莫名的喜歡鹤树,今天我就來說說nio的那些事兒赴背。
非阻塞:阻塞一直以來都是計(jì)算機(jī)解決復(fù)雜問題比較簡(jiǎn)單的思路椰拒,同樣它也是低效的代名詞,當(dāng)程序被阻塞凰荚,似乎他已經(jīng)停下來并且執(zhí)著于當(dāng)前的事情燃观,而對(duì)于新的任務(wù),它需要一直等著當(dāng)前程序結(jié)束阻塞便瑟,很多時(shí)候這種行為是沒有意義的缆毁,但又不得不那樣做。
對(duì)于IO操作更是如此到涂,所以Java1.4出來了新的IO脊框。

概念介紹

  • Channel
    可以翻譯為通道,和IO中的流對(duì)應(yīng)践啄,不同的是浇雹,Channel是雙向的,而Stream只能從一端到另一端(單向)
  • Buffer
    緩沖區(qū)屿讽,用來存放數(shù)據(jù)的區(qū)域昭灵,它有四個(gè)索引用來進(jìn)行高效的操作
    • capacity:buffer的容量
    • limit:索引能到達(dá)的地方,通常情況和容量的大小一致
    • mark:標(biāo)記伐谈,像書簽一樣烂完,保存你剛剛索引,下次很容易技能找到
    • position:位置衩婚,當(dāng)前正在操作數(shù)據(jù)的位置
  • Selector(暫不涉及)
    單線程下可以管理多個(gè)Channel的分發(fā)器

代碼實(shí)戰(zhàn)

以下借文件拷貝這一功能說明nio操作流程
@SuppressWarnings("resource")
public static void copy(File src, File dest) {
    if(src == null || dest == null) 
        throw new NullPointerException("源文件或目標(biāo)文件為空窜护!");
    if(src.isDirectory() || dest.isDirectory()) 
        throw new IllegalArgumentException("我不能對(duì)目錄進(jìn)行復(fù)制");
    if(!src.exists())
        throw new RuntimeException("源文件不存在");
    if(src.length() == 0)
        throw new RuntimeException("文件內(nèi)容為空效斑!");
    if( !dest.exists())
        try {
            dest.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    FileChannel srcChannel = null;
    FileChannel destChannel = null;
    try {
        /*
         * 創(chuàng)建FileChannel需要區(qū)分FileOutputStream和FileInputStream,
         * 如果是用FileOutputStream創(chuàng)建的Channel只能進(jìn)行寫操作而不能驚醒讀操作非春,
         * 否則會(huì)報(bào)NonReadableChannelException,反之用FileInputStream
         * 創(chuàng)建的Channel只能進(jìn)行讀操作,而不能進(jìn)行寫操作,否則會(huì)報(bào)NonWritableChannelException
         * 當(dāng)然除此之外奇昙,可以使用RandomAccessFile來替換护侮,但必須指定操作方式,就像
         * mode = "r" / "rw" 但不可以是 "w"
         * RandomAccessFile r = new RandomAccessFile(file, mode)
        */
        
        
        srcChannel = new FileInputStream(src).getChannel();
        destChannel = new FileOutputStream(dest).getChannel();
        /*
         * 創(chuàng)建創(chuàng)建Buffer储耐,這里有兩個(gè)方法allocate(int) 和 allocateDirect(int)
         * 這兩個(gè)方法表現(xiàn)在申請(qǐng)內(nèi)存的方式不一樣羊初,allocateDirect是直接向操作系統(tǒng)申請(qǐng)內(nèi)存,
         * 而allocate()是在Jvm堆中申請(qǐng)內(nèi)存什湘,當(dāng)Java從外界獲取到數(shù)據(jù)時(shí)长赞,首先經(jīng)過系統(tǒng)內(nèi)存,
         * 然后再將這部分內(nèi)存復(fù)制到Jvm內(nèi)存中闽撤,所以使用allocateDirect()會(huì)省去一步操作得哆,
         * 但是想要從操作系統(tǒng)獲取內(nèi)存是很復(fù)雜且耗時(shí)的,所以allocateDirect()不一定就快,
         * 通常分配的內(nèi)存小于1M就用allocate哟旗,更大的話就用allocateDirect比較快贩据。
         * ByteBuffer:
         * 這是最基本的byte的buffer,當(dāng)然還有其他基本類型的buffer闸餐,當(dāng)然除了Boolean
         * 不過你需要注意的是除ByteBuffer其他的buffer實(shí)際上只是ByteBuffer的不同表現(xiàn)形式饱亮,
         * 他們還是依據(jù)ByteBuffer作為真正的數(shù)據(jù)緩沖區(qū),你可以使用byteBuffer的asCharBuffer
         * 等等其它類似的方法可以將ByteBuffer轉(zhuǎn)化為其它的buffer
         */
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //read方法的意思是將channel中的數(shù)據(jù)放到buffer中舍沙,如果返回結(jié)果為-1近上,表示channel中的數(shù)據(jù)已被讀完
        while(srcChannel.read(byteBuffer) != -1) {
            /*
             * 翻轉(zhuǎn)操作 將limit索引設(shè)置為 position, 設(shè)置position為0拂铡,清空mark
             * 其實(shí)過程是這樣的戈锻,buffer的get操作和put操作都會(huì)使得position加1
             * 在channel的read操作實(shí)際上使用buffer的put操作往里面塞東西,不管有沒有塞滿
             * 都會(huì)設(shè)置limit索引和媳,設(shè)置了limit很有必要格遭,在通過buffer.get()操作或者channel.write(buffer)
             * 操作的時(shí)候會(huì)將position與limit進(jìn)行比較,如果position大于limit就會(huì)拋出BufferUnderflowException
             * 同樣如果不設(shè)置position為0留瞳,現(xiàn)在的position就等于limit拒迅,那么你通過get()將會(huì)拋出BufferUnderflowException
             * 而如果是channel.write()就不會(huì)有任何數(shù)據(jù),你可以用以下的替換flip()操作的代碼進(jìn)行測(cè)試,
             * 屆時(shí)你講明白position置于0是多么重要她倘。
             */
            //byteBuffer.limit(byteBuffer.position()); //設(shè)置limit為position
            byteBuffer.flip(); 
            //和讀操作相反璧微,它是將byteBuffer中的內(nèi)容寫入到channel中
            destChannel.write(byteBuffer);
            //清空操作(實(shí)際上它并沒有清空數(shù)據(jù),而僅僅是把那四個(gè)索引設(shè)為初始值)硬梁,因?yàn)橄乱淮蝐hannel.read()或者byteBuffer.put()操作
            //將會(huì)重新覆蓋上一次的數(shù)據(jù)
            byteBuffer.clear();
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            if(srcChannel != null)
                srcChannel.close();
            if(destChannel != null)
                destChannel.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

以上就是nio的魅力前硫,可能有人會(huì)覺得有點(diǎn)麻煩比如說flip()和clear()操作,他們其實(shí)只進(jìn)行了索引之間賦值操作荧止,只有通過索引進(jìn)行操作屹电,效率才會(huì)如此高阶剑。所以不得不使用他們,總之需要記住這些索引的功能危号,才能更好的使用它牧愁。

文件映射到內(nèi)存

出于性能考慮,在修改和創(chuàng)建大型文件的時(shí)候外莲,內(nèi)存往往承載不了太多的文件資源猪半,但是用文件映射后,可以假定整個(gè)文件都在內(nèi)存中偷线,這時(shí)候磨确,操作文件的性能可想而知。

@SuppressWarnings("resource")
private static void appendNameToFile(File file, String name) {
    if(file == null)
        throw new NullPointerException();
    if(!file.exists())
        throw new RuntimeException("文件不存在");
    try {
        FileChannel channel =  new RandomAccessFile(file, "rw").getChannel();
        try{
            /*
             * 通過channel獲取到MappedByteBuffer声邦,它是ByteBuffer的子類俐填,并且內(nèi)存是通過操作系統(tǒng)獲取的
             * 在被gc回收之前一直有限,但你并不知道什么時(shí)候gc會(huì)進(jìn)行回收操作
             * map方法有三個(gè)參數(shù)翔忽,第一個(gè)是指定讀寫模式英融,第二個(gè)參數(shù)指定從文件中的起始位置開始映射,第三個(gè)參數(shù)是從第二個(gè)
             * 參數(shù)的位置起需要映射的長(zhǎng)度為多少
             * 需要注意的是以下操作會(huì)在文件末尾 添加一個(gè)空格歇式,如果不想添加空格那么第三個(gè)參數(shù)需要設(shè)置為file.length() - 1
             * 后面的1代表第二個(gè)參數(shù)的值
             */
            //MappedByteBuffer buffer =channel.map(MapMode.READ_WRITE, 1, file.length());
            MappedByteBuffer buffer =channel.map(MapMode.READ_WRITE, file.length(), name.length());
            buffer.put(name.getBytes());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }   
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } 
}

文件鎖

Java1.4提供了給文件加鎖的功能(同步共享的文件)驶悟,用于防止不同線程的同時(shí)訪問或修改同一個(gè)文件,這包括Jvm線程和操作系統(tǒng)的本地線程材失,而且Java提供的文件鎖對(duì)象操作系統(tǒng)是可見的痕鳍,因?yàn)樗苯佑成涞搅瞬僮飨到y(tǒng)的本地鎖。

public static void addFileLock(File file) {
    if(file == null)
        throw new NullPointerException();
    if(!file.exists())
        throw new RuntimeException("文件不存在");
    try {
        @SuppressWarnings("resource")
        /*
         * 你不能使用FileInputStream龙巨,因?yàn)樗鼊?shì)必會(huì)往channel里面寫點(diǎn)東西笼呆,很自然的FileInputStream不會(huì)讓你寫,而是拋異常
         * 當(dāng)然除了tryLock還有l(wèi)ock他們的區(qū)別僅僅是非阻塞與阻塞的問題旨别,tryLock如果不能獲取鎖的話诗赌,方法直接會(huì)返回,而lock只會(huì)死等
         * 其實(shí)如果你再細(xì)心點(diǎn)秸弛,他們分別還重載了一個(gè)三參數(shù)的方法铭若,類似 tryLock(long position, long size, boolean shared)
         * 通過這個(gè)方法聲明可以看出來他是一個(gè)鎖文件局部的方法,就像鎖數(shù)據(jù)庫(kù)和單獨(dú)鎖表的區(qū)別一樣递览,你可以嘗試一下它叼屠。
         */
        FileLock fileLock = new FileOutputStream(file).getChannel().tryLock();
        if(fileLock != null) {
            System.out.println("文件已被鎖住");
            try {
                TimeUnit.SECONDS.sleep(20);
            } catch (InterruptedException e) {
            }
            fileLock.release();
            System.out.println("文件已被解鎖");
        }
    }  catch (IOException e) {
        throw new RuntimeException(e);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市绞铃,隨后出現(xiàn)的幾起案子镜雨,更是在濱河造成了極大的恐慌,老刑警劉巖儿捧,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荚坞,死亡現(xiàn)場(chǎng)離奇詭異挑宠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)西剥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亿汞,“玉大人瞭空,你說我怎么就攤上這事×莆遥” “怎么了咆畏?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吴裤。 經(jīng)常有香客問我旧找,道長(zhǎng),這世上最難降的妖魔是什么麦牺? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任钮蛛,我火速辦了婚禮安聘,結(jié)果婚禮上辛润,老公的妹妹穿的比我還像新娘蛛砰。我一直安慰自己苞冯,他們只是感情好嗡靡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布加勤。 她就那樣靜靜地躺著耕皮,像睡著了一般撑碴。 火紅的嫁衣襯著肌膚如雪仑濒。 梳的紋絲不亂的頭發(fā)上叹话,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音墩瞳,去河邊找鬼驼壶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛喉酌,可吹牛的內(nèi)容都是我干的辅柴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼瞭吃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼碌嘀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起歪架,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤股冗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后和蚪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體止状,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烹棉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怯疤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浆洗。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖集峦,靈堂內(nèi)的尸體忽然破棺而出伏社,到底是詐尸還是另有隱情,我是刑警寧澤塔淤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布摘昌,位于F島的核電站,受9級(jí)特大地震影響高蜂,放射性物質(zhì)發(fā)生泄漏聪黎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一备恤、第九天 我趴在偏房一處隱蔽的房頂上張望稿饰。 院中可真熱鬧,春花似錦露泊、人聲如沸湘纵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梧喷。三九已至,卻和暖如春脖咐,著一層夾襖步出監(jiān)牢的瞬間铺敌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工屁擅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偿凭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓派歌,卻偏偏與公主長(zhǎng)得像弯囊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胶果,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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

  • 從三月份找實(shí)習(xí)到現(xiàn)在匾嘱,面了一些公司,掛了不少早抠,但最終還是拿到小米霎烙、百度、阿里、京東悬垃、新浪游昼、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,192評(píng)論 11 349
  • 這兩天了解了一下關(guān)于NIO方面的知識(shí)尝蠕,網(wǎng)上關(guān)于這一塊的介紹只是介紹了一下基本用法烘豌,沒有系統(tǒng)的解釋NIO與阻塞、非阻...
    Ruheng閱讀 7,121評(píng)論 5 48
  • Java NIO(New IO)是從Java 1.4版本開始引入的一個(gè)新的IO API看彼,可以替代標(biāo)準(zhǔn)的Java I...
    JackChen1024閱讀 7,537評(píng)論 1 143
  • Java SE 基礎(chǔ): 封裝廊佩、繼承、多態(tài) 封裝: 概念:就是把對(duì)象的屬性和操作(或服務(wù))結(jié)合為一個(gè)獨(dú)立的整體闲昭,并盡...
    Jayden_Cao閱讀 2,099評(píng)論 0 8
  • 認(rèn)可序矩。
    碧海清天閱讀 176評(píng)論 0 0