不學無數(shù)——JAVA中NIO再深入

JAVA中NIO再深入

在上一章節(jié)的JAVA中的I/O和NIO我們學習了如何使用NIO扶叉,接下來再深入了解一下關于NIO的知識勿锅。

緩沖器內部的細節(jié)

Buffer數(shù)據(jù)可以高效地訪問及操作這些數(shù)據(jù)的四個索引組成。這四個索引是

  • mark:標記辜梳,就像游戲中設置了一個存檔一樣粱甫,可以調用reset()方法進行回歸到mark標記的地方泳叠。
  • position:位置作瞄,其實緩沖器實際上就是一個美化過的數(shù)組,從通道中讀取數(shù)據(jù)就是放到了底層的數(shù)組危纫。所以其實就像索引一樣宗挥。所以positon變量跟蹤已經寫了多少數(shù)據(jù)乌庶。
  • limit:界限,即表明還有多少數(shù)據(jù)需要取出契耿,或者還有多少空間能夠寫入瞒大。
  • capacity:容量,表明緩沖器中可以存儲的最大容量搪桂。

在緩沖器中每一個讀寫操作都會改變緩沖器的狀態(tài)透敌,用于反應所發(fā)生的變化。通過記錄和跟蹤這些變化踢械,緩沖器就能夠內部地管理自己的資源酗电。下面是用于設置和復位索引以及查詢其索引值的方法

方法名 解釋
capacity() 返回緩沖器的容量
clear() 清空緩沖器,將position設置為0内列,limit設置容量撵术。調用此方法復寫緩沖器
flip() 將limit設置為position,position設置為0.此方法用于準備從緩沖區(qū)讀取已經寫入的數(shù)據(jù)
limit() 返回limit值
limit(int lim) 設置limit值
mark() 將mark設置為positon
position() 返回position的值
position(int pos) 設置postion的值
remaining() 返回limit-position的值

接下來我們寫個例子模擬這四個索引的變化情況话瞧,例如有一個字符串BuXueWuShu嫩与。我們交換相鄰的字符。

    private static void symmetricScranble(CharBuffer buffer){
        while (buffer.hasRemaining()){
            buffer.mark();
            char c1 = buffer.get();
            char c2 = buffer.get();
            buffer.reset();
            buffer.put(c2).put(c1);
        }
    }

    public static void main(String[] args) {
        char [] data = "BuXueWuShu".toCharArray();
        ByteBuffer byteBuffer = ByteBuffer.allocate(data.length*2);
        CharBuffer charBuffer = byteBuffer.asCharBuffer();
        charBuffer.put(data);
        System.out.println(charBuffer.rewind());
        symmetricScranble(charBuffer);
        System.out.println(charBuffer.rewind());
        symmetricScranble(charBuffer);
        System.out.println(charBuffer.rewind());
    }

rewind()方法是將position設為0 交排,mark設為-1

在剛進入symmetricScranble ()方法時的各個索引如下圖所示

1

然后第一次調用了mark()方法以后就相當于給mark賦值了划滋,相當于在此設置了一個回檔點。此時索引如下所示

2

然后每次調用get()方法Position索引都會改變埃篓,在第一次調用了兩次get()方法以后古毛,各個索引如下

3

然后調用了reset()方法另Position=Mark,此時的索引如下

4

然后每次調用put()方法也會改變Position索引的值都许,

5

注意此時前兩個字符已經互換了位置稻薇。然后在第二輪while開始再次改變了Mark索引的值,各個索引如下

6

此時我們應該就知道前面我們說的調用clear()方法并不會清除緩沖器里面的數(shù)據(jù)的原因了胶征,因為只是將其索引變了而已塞椎。

內存映射文件

內存映射文件不是Java引入的概念,而是操作系統(tǒng)提供的一種功能睛低,大部分操作系統(tǒng)都支持案狠。

內存映射文件允許我們創(chuàng)建和修改那些因為太大而不能放入內存的文件。有了內存映射文件钱雷,我們就可以假定整個文件都放在內存中骂铁,而且可以完全將其視為非常大的數(shù)組進行訪問。所以對于文件的操作就變?yōu)榱藢τ趦却嬷械淖止?jié)數(shù)組的操作罩抗,然后對于字節(jié)數(shù)組的操作會映射到文件中拉庵。這種映射可以映射整個文件,也可以只映射文件中的一部分套蒂。什么時候字節(jié)數(shù)組中的的操作會映射到文件上呢钞支?這是由操作系統(tǒng)內部決定的茫蛹。

內存放不下整個文件也不要緊,操作系統(tǒng)會自動進行處理烁挟,將需要的內容讀到內存婴洼,將修改的內容保存到硬盤,將不再使用的內存釋放撼嗓。

如何用NIO將文件映射到內存中呢柬采?下面有個小例子表示將文件的前1024個字節(jié)映射到內存中。

FileChannel fileChannel = new FileInputStream("").getChannel();
MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);

創(chuàng)建一個內存映射文件只需要在通道中調用map()方法即可且警,MapMode有以下三個參數(shù)

  • READ_ONLY:創(chuàng)建一個只讀的映射文件
  • READ_WRITE:創(chuàng)建一個既能讀也能寫的映射文件
  • PRIVATE:創(chuàng)建一個寫時拷貝(copy-on-write)的映射文件

我們可以簡單的對比一下用內存映射文件對文件進行讀寫操作和用緩存Buffer對文件進行讀寫操作的速度比較警没。

  public static void main(String[] args) throws IOException {
        String fileName="/Users/hupengfei/Downloads/a.sql";
        long t1=System.currentTimeMillis();
        FileChannel fileChannel = new RandomAccessFile(fileName,"rw").getChannel();
        IntBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileChannel.size()).asIntBuffer();
        map.put(0);
        for (int i = 1; i < 50590; i++) {
            map.put(map.get(i-1));
        }
        fileChannel.close();
        long t=System.currentTimeMillis()-t1;
        System.out.println("Mapped Read/Write:"+t);

        long t2=System.currentTimeMillis();
        RandomAccessFile randomAccessFile = new RandomAccessFile(new File(fileName),"rw");
        randomAccessFile.writeInt(1);
        for (int i = 0 ; i<50590;i++){
            randomAccessFile.seek(randomAccessFile.length()-4);
            randomAccessFile.writeInt(randomAccessFile.readInt());
        }
        randomAccessFile.close();
        long t22=System.currentTimeMillis()-t2;
        System.out.println("Stream Read/Write:"+t22);
    }

發(fā)現(xiàn)打印如下

Mapped Read/Write:29
Stream Read/Write:2439

文件越大,那么這個差異會更明顯振湾。

文件加鎖

在JDK1.4中引入了文件加鎖的機制杀迹,它允許我們同步的訪問某個作為共享資源的文件。對于同一文件競爭的兩個線程可能是來自于不同的操作系統(tǒng)押搪,也可能是不同的進程树酪,也可能是相同的進程,例如Java中兩個線程對于文件的競爭大州。文件鎖對于其他的操作系統(tǒng)的進程是可見的续语,因為文件加鎖是直接映射到了本地操作系統(tǒng)的加鎖工具。

下面舉了一個簡單的關于文件加鎖的例子

    public static void main(String[] args) throws IOException, InterruptedException {
        FileOutputStream fileOutputStream = new FileOutputStream("/Users/hupengfei/Downloads/a.sql");
        FileLock fileLock = fileOutputStream.getChannel().tryLock();
        if (fileLock != null){
            System.out.println("Locked File");
            TimeUnit.MICROSECONDS.sleep(100);
            fileLock.release();
            System.out.println("Released Lock");
        }
        fileLock.close();
    }

通過對FileChannel調用tryLock()或者lock()方法厦画,就可以獲得整個文件的FileLock

  • tryLock():是非阻塞的疮茄,如果不能獲得鎖,那么他就會直接從方法調用中返回
  • lock():是阻塞的根暑,它要阻塞進程直到鎖可以獲得為止

調用FileLock.release()可以釋放鎖力试。

當然也可以通過以下的方式對于文件的部分進行上鎖

tryLock(long position,long size,boolean shared)
lock(long position,long size,boolean shared)

對于加鎖的區(qū)域是通過positionsize進行限定的,而第三個參數(shù)指定是否為共享鎖排嫌。無參數(shù)的加鎖方法會對整個文件進行加鎖畸裳,甚至文件變大以后也是如此。其中鎖的類型是獨占鎖還是共享鎖可以通過FileLock.isShared()進行查詢淳地。

參考文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末怖糊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颇象,更是在濱河造成了極大的恐慌伍伤,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遣钳,死亡現(xiàn)場離奇詭異扰魂,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門阅爽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荐开,你說我怎么就攤上這事付翁。” “怎么了晃听?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵百侧,是天一觀的道長。 經常有香客問我能扒,道長佣渴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任初斑,我火速辦了婚禮辛润,結果婚禮上,老公的妹妹穿的比我還像新娘见秤。我一直安慰自己砂竖,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布鹃答。 她就那樣靜靜地躺著乎澄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪测摔。 梳的紋絲不亂的頭發(fā)上置济,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音锋八,去河邊找鬼浙于。 笑死,一個胖子當著我的面吹牛挟纱,可吹牛的內容都是我干的路媚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼樊销,長吁一口氣:“原來是場噩夢啊……” “哼整慎!你這毒婦竟也來了?” 一聲冷哼從身側響起围苫,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤裤园,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后剂府,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拧揽,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了淤袜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痒谴。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铡羡,靈堂內的尸體忽然破棺而出积蔚,到底是詐尸還是另有隱情,我是刑警寧澤烦周,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布尽爆,位于F島的核電站,受9級特大地震影響读慎,放射性物質發(fā)生泄漏漱贱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一夭委、第九天 我趴在偏房一處隱蔽的房頂上張望幅狮。 院中可真熱鬧,春花似錦株灸、人聲如沸彪笼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽配猫。三九已至,卻和暖如春杏死,著一層夾襖步出監(jiān)牢的瞬間泵肄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工淑翼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腐巢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓玄括,卻偏偏與公主長得像冯丙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子遭京,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內容