DANCE WITH JAVA

          開發(fā)出高質(zhì)量的系統(tǒng)

          常用鏈接

          統(tǒng)計(jì)

          積分與排名

          好友之家

          最新評(píng)論

          探索Java NIO的歷程

          探索Java NIO的歷程
          前段時(shí)間有些時(shí)間,打算看看NIO的東西,本來(lái)以為很快可以了解的東西,卻用了很多時(shí)間。
          首先Goole NIO可以看到很多的教程,非阻塞,Buffer,內(nèi)存映射,塊讀取前三個(gè)很快就有所了解
          嘗試著寫了些小程序,學(xué)習(xí)東西的時(shí)候總喜歡寫點(diǎn)小例子。
          唯獨(dú)塊讀取沒有找到對(duì)應(yīng)的東西。(在過程中,主要看了IBM 的NIO入門)

          首先,IBM NIO入門中的語(yǔ)句
          --------------------------------------------------------------------------------
          原來(lái)的 I/O 庫(kù)(在 java.io.*中) 與 NIO 最重要的區(qū)別是數(shù)據(jù)打包和傳輸?shù)姆绞健U缜懊嫣岬降模?br />原來(lái)的 I/O 以流的方式處理數(shù)據(jù),而 NIO 以塊的方式處理數(shù)據(jù)。 面向流 的 I/O 系統(tǒng)一次一個(gè)字節(jié)地處
          理數(shù)據(jù)。一個(gè)輸入流產(chǎn)生一個(gè)字節(jié)的數(shù)據(jù),一個(gè)輸出流消費(fèi)一個(gè)字節(jié)的數(shù)據(jù)。為流式數(shù)據(jù)創(chuàng)建過濾器非常容易。鏈接幾個(gè)過濾器,以便每個(gè)過濾器只負(fù)責(zé)單個(gè)復(fù)雜處理機(jī)制的一部分,這樣也是相對(duì)簡(jiǎn)單的。不利的一面是,面向流的 I/O 通常相當(dāng)慢。 一個(gè) 面向塊 的 I/O 系統(tǒng)以塊的形式處理數(shù)據(jù)。每一個(gè)操作都在一步中產(chǎn)生或者消費(fèi)一個(gè)數(shù)據(jù)塊。按塊處理數(shù)據(jù)比按(流式的)字節(jié)處理數(shù)據(jù)要快得多。但是面向塊的 I/O 缺少一些面向流的I/O 所具有的優(yōu)雅性和簡(jiǎn)單性。
          --------------------------------------------------------------------------------
          首先簡(jiǎn)單的印象是NIO快,所以想寫個(gè)程序驗(yàn)證一下.如下復(fù)制:

          ?

          ?1 public ? static ? void ?test2(String?name1,?String?name2)? {
          ?2 ???????? long ?start? = ?System.currentTimeMillis();
          ?3 ???????? try ? {
          ?4 ????????????FileInputStream?fis? = ? new ?FileInputStream(name1);
          ?5 ????????????FileOutputStream?fos? = ? new ?FileOutputStream(name2);
          ?6 ???????????? byte []?buf? = ? new ? byte [ 8129 ];
          ?7 ???????????? while ?( true )? {????????????????
          ?8 ???????????????? int ?n? = ?fis.read(buf);
          ?9 ???????????????? if ?(n? == ? - 1 )? {
          10 ???????????????????? break ;
          11 ????????????????}

          12 ????????????????fos.write(buf, 0 ,n);
          13 ????????????}

          14 ????????????fis.close();
          15 ????????????fos.close();
          16 ????????}
          ? catch ?(Exception?e)? {
          17 ????????????e.printStackTrace();
          18 ????????}

          19 ???????? long ?end? = ?System.currentTimeMillis();
          20 ???????? long ?time? = ?end? - ?start;
          21 ????????System.out.println(time);
          22 ????}

          23 ????
          24 ???? public ? static ? void ?test3(String?name1,?String?name2)? {
          25 ???????? long ?start? = ?System.currentTimeMillis();
          26 ???????? try ? {
          27 ????????????FileInputStream?in? = ? new ?FileInputStream(name1);
          28 ????????????FileOutputStream?out? = ? new ?FileOutputStream(name2);
          29 ????????????FileChannel?fc1? = ?in.getChannel();
          30 ????????????FileChannel?fc2? = ?out.getChannel();
          31 ????????????ByteBuffer?bb? = ?ByteBuffer.allocate( 8129 );
          32 ???????????? while ?( true )? {
          33 ????????????????bb.clear();
          34 ???????????????? int ?n? = ?fc1.read(bb);
          35 ???????????????? if ?(n? == ? - 1 )? {
          36 ???????????????????? break ;
          37 ????????????????}

          38 ????????????????bb.flip();
          39 ????????????????fc2.write(bb);
          40 ????????????}

          41 ????????????fc1.close();
          42 ????????????fc2.close();
          43 ????????}
          ? catch ?(IOException?e)? {
          44
          45 ????????}

          46 ???????? long ?end? = ?System.currentTimeMillis();
          47 ???????? long ?time? = ?end? - ?start;
          48 ????????System.out.println(time);
          49 ????}

          本以為可以結(jié)束,結(jié)果測(cè)試結(jié)果出乎意料,函數(shù)一比函數(shù)二要快,就是說Old IO快于NIO ,從此
          ?也就開始了整個(gè)過程:
          ?為了了解這個(gè)問題,仔細(xì)搜索并仔細(xì)再看IBM 的NIO教程,看到如下這段話
          ?---------------------------------------------
          ?在 JDK 1.4 中原來(lái)的 I/O 包和 NIO 已經(jīng)很好地集成了。 java.io.* 已經(jīng)以 NIO 為基礎(chǔ)重新實(shí)現(xiàn)了,
          ?所以現(xiàn)在它可以利用 NIO 的一些特性。例如, java.io.* 包中的一些類包含以塊的形式讀寫數(shù)據(jù)的方法,
          ?這使得即使在更面向流的系統(tǒng)中,處理速度也會(huì)更快。 也可以用 NIO 庫(kù)實(shí)現(xiàn)標(biāo)準(zhǔn) I/O 功能。例如,
          ?可以容易地使用塊 I/O 一次一個(gè)字節(jié)地移動(dòng)數(shù)據(jù)。但是正如您會(huì)看到的,NIO 還提供了原 I/O 包中所沒有的許多好處。
          ??? ---------------------------------------------
          ??? 所以我想,是否因?yàn)镮nputStream中使用了塊讀取實(shí)現(xiàn)了呢,所以進(jìn)入JDK1.4中的InputStream中
          ??? 看看source,首先引起我注意的是read函數(shù),當(dāng)參數(shù)是一個(gè)byte數(shù)組的時(shí)候,直接調(diào)用的native實(shí)現(xiàn)
          ??? 難道是這個(gè),為了驗(yàn)證,下載了一個(gè)JDK1.3下來(lái),發(fā)現(xiàn)JDK1.3是一樣的。
          ???
          ??? 繼續(xù),我想是否是JVM底層實(shí)現(xiàn)了塊讀取呢,為了證明這個(gè)我用JDK1.3和JDK1.4同時(shí)實(shí)現(xiàn)了類似的函數(shù),??? 測(cè)試的結(jié)果再次出乎意料,性能相差不大.那就不是這個(gè)了。
          ? 為此多方查找資料,未果,為此多寫幾個(gè)函數(shù),好好測(cè)試一下IO的不同。于是有了如下的一些函數(shù)

          ??1 // ?exec
          ??2 ???? public ? static ? void ?test1(String?name1,?String?name2)? {
          ??3 ???????? long ?start? = ?System.currentTimeMillis();
          ??4 ???????? try ? {
          ??5 ????????????String?cmd? = ? " cmd?/c?copy?d:\\out1.txt?d:\\out2.txt " ;
          ??6 ????????????System.out.println(cmd);
          ??7 ????????????Process?p? = ?Runtime.getRuntime().exec(cmd);÷
          ??8 ????????????p.waitFor();
          ??9 ????????}
          ? catch ?(Exception?e)? {
          ?10 ????????????e.printStackTrace();
          ?11 ????????}

          ?12 ???????? long ?end? = ?System.currentTimeMillis();
          ?13 ???????? long ?time? = ?end? - ?start;
          ?14 ????????System.out.println(time);
          ?15 ????}

          ?16
          ?17 ???? // ?old?io
          ?18 ???? public ? static ? void ?test2(String?name1,?String?name2)? {
          ?19 ???????? long ?start? = ?System.currentTimeMillis();
          ?20 ???????? try ? {
          ?21 ????????????FileInputStream?fis? = ? new ?FileInputStream(name1);
          ?22 ????????????FileOutputStream?fos? = ? new ?FileOutputStream(name2);
          ?23 ???????????? while ?( true )? {
          ?24 ???????????????? byte []?buf? = ? new ? byte [ 8129 ];
          ?25 ???????????????? int ?n? = ?fis.read(buf);
          ?26 ???????????????? if ?(n? == ? - 1 )? {
          ?27 ???????????????????? break ;
          ?28 ????????????????}

          ?29 ????????????????fos.write(buf);
          ?30 ????????????}

          ?31 ????????????fis.close();
          ?32 ????????????fos.close();
          ?33 ????????}
          ? catch ?(Exception?e)? {
          ?34 ????????????e.printStackTrace();
          ?35 ????????}

          ?36 ???????? long ?end? = ?System.currentTimeMillis();
          ?37 ???????? long ?time? = ?end? - ?start;
          ?38 ????????System.out.println(time);
          ?39 ????}

          ?40
          ?41 ???? // ?new?io
          ?42 ???? public ? static ? void ?test3(String?name1,?String?name2)? {
          ?43 ???????? long ?start? = ?System.currentTimeMillis();
          ?44 ???????? try ? {
          ?45 ????????????FileInputStream?in? = ? new ?FileInputStream(name1);
          ?46 ????????????FileOutputStream?out? = ? new ?FileOutputStream(name2);
          ?47 ????????????FileChannel?fc1? = ?in.getChannel();
          ?48 ????????????FileChannel?fc2? = ?out.getChannel();
          ?49 ????????????ByteBuffer?bb? = ?ByteBuffer.allocate( 8129 );
          ?50 ???????????? while ?( true )? {
          ?51 ????????????????bb.clear();
          ?52 ???????????????? int ?n? = ?fc1.read(bb);
          ?53 ???????????????? if ?(n? == ? - 1 )? {
          ?54 ???????????????????? break ;
          ?55 ????????????????}

          ?56 ????????????????bb.flip();
          ?57 ????????????????fc2.write(bb);
          ?58 ????????????}

          ?59 ????????????fc1.close();
          ?60 ????????????fc2.close();
          ?61 ????????}
          ? catch ?(IOException?e)? {
          ?62
          ?63 ????????}

          ?64 ???????? long ?end? = ?System.currentTimeMillis();
          ?65 ???????? long ?time? = ?end? - ?start;
          ?66 ????????System.out.println(time);
          ?67 ????}

          ?68
          ?69 ???? // ?fast?copy
          ?70 ???? public ? static ? void ?test4(String?name1,?String?name2)? {
          ?71 ???????? long ?start? = ?System.currentTimeMillis();
          ?72 ???????? try ? {
          ?73 ????????????FileInputStream?in? = ? new ?FileInputStream(name1);
          ?74 ????????????;
          ?75 ????????????FileOutputStream?out? = ? new ?FileOutputStream(name2);
          ?76 ????????????;
          ?77 ????????????FileChannel?fc1? = ?in.getChannel();
          ?78 ????????????FileChannel?fc2? = ?out.getChannel();
          ?79 ????????????ByteBuffer?bb? = ?ByteBuffer.allocateDirect( 8129 );
          ?80 ???????????? while ?( true )? {
          ?81 ????????????????bb.clear();
          ?82 ???????????????? int ?n? = ?fc1.read(bb);
          ?83 ???????????????? if ?(n? == ? - 1 )? {
          ?84 ???????????????????? break ;
          ?85 ????????????????}

          ?86 ????????????????bb.flip();
          ?87 ????????????????fc2.write(bb);
          ?88 ????????????}

          ?89 ????????????fc1.close();
          ?90 ????????????fc2.close();
          ?91 ????????}
          ? catch ?(IOException?e)? {
          ?92
          ?93 ????????}

          ?94 ???????? long ?end? = ?System.currentTimeMillis();
          ?95 ???????? long ?time? = ?end? - ?start;
          ?96 ????????System.out.println(time);
          ?97 ????}

          ?98
          ?99 ???? // ?transfer?,read?and?write?at?same?time
          100 ???? public ? static ? void ?test5(String?name1,?String?name2)? {
          101 ???????? long ?start? = ?System.currentTimeMillis();
          102 ???????? try ? {
          103 ????????????RandomAccessFile?raf1? = ? new ?RandomAccessFile(name1,? " rw " );
          104 ????????????RandomAccessFile?raf2? = ? new ?RandomAccessFile(name2,? " rw " );
          105 ????????????FileChannel?fc1? = ?raf1.getChannel();
          106 ????????????FileChannel?fc2? = ?raf2.getChannel();
          107 ????????????fc1.transferTo( 0 ,?raf1.length(),?fc2);
          108 ????????????fc1.close();
          109 ????????????fc2.close();
          110 ????????}
          ? catch ?(Exception?e)? {
          111 ????????????e.printStackTrace();
          112 ????????}

          113 ???????? long ?end? = ?System.currentTimeMillis();
          114 ???????? long ?time? = ?end? - ?start;
          115 ????????System.out.println(time);
          116 ????}

          首先測(cè)試的文件是一個(gè)30幾M的文件,測(cè)試結(jié)果出乎意料,是否因?yàn)槲募∧??用個(gè)200M的文件再次
          ?測(cè)試一下,結(jié)果更匪夷所思,transfor的方式快些,exec的方式快些,Old IO和NIO相差不大,而且
          ?稍快與NIO,拿到這個(gè)結(jié)果真讓人氣餒,再次查找資料,但都沒有解決自己的疑問,這個(gè)時(shí)候看到了
          ?Think In Java第三版上講到NIO,更讓我興奮的是我看到如下的話,大概意思吧,原文記不住了
          ?"Java NIO在文件讀取和網(wǎng)絡(luò)方面有很大的性能提高,網(wǎng)絡(luò)方面不說,這里說說文件操作"看到這段話
          ?真是高興,因?yàn)門hink In Java一向都是有很多寫的很好的小例子,懷著這種心情,往下看,終于找到了
          ?一個(gè)顯示性能差別小例子,但讓我哭笑不得的是,作者居然是使用的內(nèi)存映射的例子。類似這樣
          ?RandomAccessFile raf1 = new RandomAccessFile(name1, "rw");
          ???FileChannel fc1 = raf1.getChannel();
          ???MappedByteBuffer mbb = fc1.map(FileChannel.MapMode.READ_WRITE, 0,
          ?????1024);
          ?使用內(nèi)存映射來(lái)和傳統(tǒng)的IO來(lái)對(duì)比讀寫,本來(lái)就是個(gè)不公平的事情。內(nèi)存映射的讀取實(shí)際是內(nèi)存讀寫
          ?和傳統(tǒng)IO比肯定有很大差距。到現(xiàn)在,從開始NIO到現(xiàn)在已經(jīng)有1周的時(shí)間了,每天我都會(huì)在工作之余拿出?1-2個(gè)小時(shí)看看NIO,但這個(gè)問題越來(lái)越迷離。
          ?
          ?今天有時(shí)間把這個(gè)過程寫出來(lái),一方面感覺過程無(wú)奈,寫出來(lái)留個(gè)紀(jì)念,另一方面希望和大家交流一下,?如果你沒看過NIO或者沒有作過這個(gè)方面的嘗試,如果你有興趣可以一起探討,如果你了解這個(gè)問題,請(qǐng)?不吝賜教,或給個(gè)提示,或者告訴我我的測(cè)試錯(cuò)在什么地方,不勝感謝。同時(shí),我會(huì)繼續(xù)這個(gè)問題,直到?找到答案。然后會(huì)把答案放上來(lái)共享。

          posted on 2006-11-18 23:56 dreamstone 閱讀(11618) 評(píng)論(16)  編輯  收藏 所屬分類: jdk相關(guān)

          評(píng)論

          # re: 探索Java NIO的歷程 2006-11-19 15:49 NCindy

          NIO的目的不是在單個(gè)IO的時(shí)候快過普通I/O,而是在并發(fā)多個(gè)I/O的時(shí)候減少線程的使用量,從而提高整個(gè)系統(tǒng)的可伸縮性。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-19 19:34 dreamstone

          NIO的目的是加速IO,并發(fā)多個(gè)IO時(shí)候減少線程使用量是其中的一個(gè)很重要的部分,這不排除,文章開始我已經(jīng)說了。但是在驗(yàn)證過程中,其它的問題例如非阻塞,例如內(nèi)存映射,有大量的例子,很容易讓人理解,但我提到的問題,并沒有找到答案。我的意思不是說這個(gè)重要,而是疑問在這里。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-19 21:37 NCindy

          看了兩遍,還是不清楚你的問題是啥,呵呵。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-19 21:44 dreamstone

          謝謝你指出問題,也許我說的不夠清楚.
          我的疑問是:
          很多地方(IBM NIO入門教程,Think In Java, google到的教材)都說NIO的文件操作有很大幅度的性能提高.但是我自己寫程序證明不出什么地方提高了,我還沒找到方法可以證明性能提高了。我學(xué)習(xí)東西的一個(gè)習(xí)慣是寫一些小的例子,證明事實(shí)確實(shí)如此,但塊讀取提高性能方面,我確實(shí)沒有做到,寫出的程序要不互相矛盾,要不是反例。
            回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-19 21:59 NCindy

          其實(shí)讀取文件,除非JDK代碼有問題,否則怎么也不會(huì)有大幅性能提升了。讀文件,完全是磁盤性能問題了。

          下面這段話(來(lái)自Java? I/O, 2nd Edition)也許能解決你部分問題:
          Nonblocking I/O is primarily relevant to network connections. Pipe channels that move data between two threads also support nonblocking I/O. File channels don't support it at all because file access doesn't block nearly as often as network channels do, and most modern disk controllers can fill a CPU with data fast enough to keep it satisfied. Furthermore, it's uncommon for one program to read or write hundreds of files simultaneously. However, on network servers, this usage pattern is the rule, not the exception.


            回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-19 22:08 dreamstone

          首先,謝謝你幫忙解決我的疑問。:)
          然后,這個(gè)問題如果是這樣的話,所有的現(xiàn)象就說的通了,我再看看。謝謝  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-20 10:20 demo java

          JDK1.4以上的IO讀取底層使用的實(shí)際上都是NIO,要比較得拿1.3里的OLD IO去比較。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-20 14:18 dreamstone

          樓上看我的文章,我確是拿jdk1.3比較了。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-28 11:09 roygbip

          呵呵,你一定是沒有用對(duì)jdk吧我把你上面方法的2,3,4在jdk1.5的環(huán)境中測(cè)試拷貝一個(gè)20M的文件,時(shí)間,分別是297,453,406,不過方法2在jdk1.3的環(huán)境中用時(shí)卻是3969。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2006-11-28 11:37 dreamstone

          首先jdk的使用是肯定沒錯(cuò)的,我測(cè)試了很多次,而且反復(fù)確認(rèn)了這個(gè)問題。你的這個(gè)數(shù)據(jù)我測(cè)試出來(lái)過,首先說說一些測(cè)試時(shí)候的注意的幾個(gè)問題吧。
          1,不能直接一個(gè)main函數(shù)調(diào)用所有的test(),因?yàn)闀?huì)有內(nèi)存回收的問題。可以嘗試把執(zhí)行順序倒過來(lái),會(huì)發(fā)現(xiàn)所有的測(cè)試數(shù)據(jù)會(huì)有大變化
          2,可以在每個(gè)函數(shù)之間加上System.gc(),但是依然會(huì)有問題,大概的規(guī)律是第一次執(zhí)行慢,第二次快,以后還有快的幾次,然後忽然又變慢。這個(gè)不是虛擬機(jī)load的問題。
          3,可以做如下測(cè)試,只執(zhí)行一個(gè)函數(shù),用手動(dòng)執(zhí)行5-7次,點(diǎn)的快和慢得到的結(jié)果很大差距。
          4,建議用大一點(diǎn)的文件200m,或者更大,因?yàn)檫@樣可以漸少一些特殊因素的影響
          5,多次測(cè)試,去掉怪異的數(shù)值,平均值
          6,這個(gè)問題我現(xiàn)在發(fā)現(xiàn)的最穩(wěn)妥的辦法是,執(zhí)行test1(),等半分鐘,再執(zhí)行另一個(gè),雖然這個(gè)方法是笨的不行,但是確是得出的數(shù)據(jù)穩(wěn)定些,其他的辦法很難得出
          正確的數(shù)據(jù)。
          7,我也嘗試了在linux來(lái)測(cè)試,效果也不明顯。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2007-07-28 00:06 he

          在單cpu和單硬盤的情況下,你想想,是多線程讀寫文件快,還是單線程快.
          NIO主要用于提高并發(fā)處理的能力(能同時(shí)服務(wù)于更多的客戶,而資源消耗最小),而不是提升速度.  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2008-05-06 16:05 ztk

          學(xué)習(xí)了
            回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2009-03-05 17:35 zod

          內(nèi)存映射難道不是NIO的長(zhǎng)處嗎
            回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2009-05-03 22:37 C.

          學(xué)習(xí)了  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2009-06-30 17:41 吳冬冬

          你這也叫使用NIO?
          很搞笑啊,多看看API,不是我打擊你。  回復(fù)  更多評(píng)論   

          # re: 探索Java NIO的歷程 2009-08-16 16:46 QQ124294272

          是啊,selector也沒有  回復(fù)  更多評(píng)論   

          主站蜘蛛池模板: 富锦市| 富顺县| 城步| 巩义市| 镇康县| 桐柏县| 五华县| 玛曲县| 新沂市| 靖宇县| 咸丰县| 波密县| 浏阳市| 马公市| 乌拉特后旗| 响水县| 蚌埠市| 安庆市| 永清县| 石狮市| 建阳市| 泌阳县| 石首市| 东阳市| 河间市| 金平| 泾阳县| 天门市| 平乐县| 泗阳县| 体育| 广州市| 原平市| 南部县| 开鲁县| 如东县| 霍城县| 莱芜市| 马龙县| 灵寿县| 漳州市|