您好,我是湘王,這是我的博客園,歡迎您來,歡迎您再來~
之前在呼叫Channel的代碼中,使用了一個名叫ByteBuffer類,它是Buffer的子類,這個叫Buffer的類是專門用來解決高速設備與低速設備之間速度不匹配的問題的,也可以減少資料庫的讀寫次數,
它又分為輸入緩沖區和輸出緩沖區,
很多初學者不明白「緩沖」和「快取」的區別,我嘗試著用大白話解釋下:
1、緩沖區需要定期進行重繪、清空、重置等操作,這些操作快取可能并不需要,比如做飯時,砧板就是緩沖,冰箱就是快取,因為從菜冰箱取出來到下鍋,需要不停地切、拍、剁,每次都要清空了才能做下一道菜,而冰箱是不用定期清空、重置的(除非停電,東西都壞了);

2、緩沖區核心作用是解耦設備間的速度制約,成為設備間的「緩沖」,而快取則是用來加快讀取的速度,減少重新計算或者重新從資料庫獲取的次數,相比于每做一道菜,都從菜場去買,顯然放在冰箱要快得多;而相比于每次做菜都從冰箱拿,當然從砧板上順手拿要更快一些,也就是:「菜場買菜速度(磁盤) < 冰箱拿菜速度(快取) < 砧板拿菜速度(緩沖區)」,就是這么個關系;
3、緩沖區側重于速度,側重于寫,而快取側重次數,側重于讀,就像砧板側重于切菜,而冰箱側重于存放;
4、現在的快取一般都很大,甚至可以達到TB級別(1TB=1024GB),緩沖是不可能這么大的(當然你也可以把砧板搞成冰箱那么大,反正我還沒見過這種-_-!),
以后再見到緩沖、快取的時候,就可以拿家里的砧板和冰箱做對比,
在NIO中有八種型別的緩沖區:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer, ShortBuffer和MappedByteBuffer,前七種分別對應基本資料型別,MappedByteBuffer專門用于記憶體映射,

緩沖Buffer區實際上也是一個容器,一個由連續陣列/集合組成的容器,Channel提供從檔案、網路讀取資料的渠道,但是讀寫的資料都必須經過Buffer,
向Buffer中寫入資料的程序是:
1、從Channel寫入資料到Buffer:channel.read(buf)
2、呼叫Buffer的put()方法:buf.put(Object)
從Buffer中讀取資料的程序是:
1、從Buffer讀取資料到Channel:channel.write(buf)
2、呼叫Buffer的get()方法:buf.get()
讀寫程序大概就是這樣的:

還是昨天那句話:如果你在大廠是自研類RPC系統或類MQ中間件的,那這個一定要精通;否則理解就好,不必死磕,Buffer看到這里其實已經足夠了,至于說:Buffer的屬性、使用Buffer的步驟、JVM怎么在記憶體創建緩沖區等等,這些應該都是面霸必修課,但開發中極少用到,
還是用代碼來說,
Buffer的常用方法:
// 分配JVM間接緩沖區 ByteBuffer buffer = ByteBuffer.allocate(32); System.out.println("buffer初始狀態: " + buffer); // 將position設回8 buffer.position(8); System.out.println("buffer設定后狀態: " + buffer); System.out.println("測驗reset ======================>>>"); // clear()方法,position將被設回0,limit被設定成capacity的值 buffer.clear(); System.out.println("buffer clear后狀態: " + buffer); // 設定這個緩沖區的位置 buffer.position(5); // 將此緩沖區的標記設定5 // 如果沒有buffer.mark();這句話會報錯 buffer.mark(); buffer.position(10); System.out.println("reset前狀態: " + buffer); // 將此緩沖區的位置重置為先前標記的位置(buffer.position(5)) buffer.reset(); System.out.println("reset后狀態: " + buffer); System.out.println("測驗get ======================>>>"); buffer = ByteBuffer.allocate(32); buffer.put((byte) 'x').put((byte) 'i').put((byte) 'a').put((byte) 'n').put((byte) 'g'); System.out.println("flip前狀態: " + buffer); // 轉換為讀模式 buffer.flip(); System.out.println("get前狀態: " + buffer); System.out.println((char) buffer.get()); System.out.println("get后狀態: " + buffer); System.out.println("測驗put ======================>>>"); ByteBuffer pb = ByteBuffer.allocate(32); System.out.println("put前狀態: " + pb + ", put前資料: " + new String(pb.array())); System.out.println("put后狀態: " + pb.put((byte) 'w') + ", put后資料: " + new String(pb.array())); System.out.println(pb.put(3, (byte) '3')); // put(3, (byte) '3')并不改變position的位置,但put((byte) '3')會 System.out.println("put(3, '3')后狀態: " + pb + ", 資料: " + new String(pb.array())); // 這里的buffer是 xiang[pos=1 lim=5 cap=32] System.out.println("buffer疊加前狀態: " + buffer + ", buffer疊加前資料: " + new String(buffer.array())); // buffer.put(pb);會拋例外BufferOverflowException pb.put(buffer); // 疊加后資料是wiang,因為buffer的position=1 System.out.println("put(buffer)后bb狀態: " + pb + ", buffer疊加后資料: " + new String(pb.array())); // 重新讀取buffer中所有資料 System.out.println("測驗rewind ======================>>>"); buffer.clear(); buffer.position(10); System.out.println("buffer當前狀態: " + buffer); // 回傳此緩沖區的限制 buffer.limit(15); System.out.println("limit后狀態: " + buffer); // 把position設為0,mark設為-1,不改變limit的值 buffer.rewind(); System.out.println("rewind后狀態: " + buffer); // 將所有未讀的資料拷貝到Buffer起始處,然后將position設到最后一個未讀元素正后面 System.out.println("測驗compact ======================>>>"); buffer.clear(); buffer.put("abcd".getBytes()); System.out.println("compact前狀態: " + buffer); System.out.println(new String(buffer.array())); // limit=position;position=0;mark=-1; 翻轉,也就是讓flip之后的position到limit這塊區域變成之前的0到position這塊 // 翻轉就是將一個處于存資料狀態的緩沖區變為一個處于準備取資料的狀態,或者相反 buffer.flip(); System.out.println("flip后狀態: " + buffer); // get()方法:相對讀,從position位置讀取一個byte,并將position+1,為下次讀寫作準備 System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println((char) buffer.get()); System.out.println("三次呼叫get后: " + buffer); System.out.println(new String(buffer.array())); // 把從position到limit中的內容移到0到limit-position的區域內 // position和limit的取值也分別變成limit-position、capacity // 如果先將positon設定到limit,再compact,那么相當于clear() buffer.compact(); System.out.println("compact后狀態: " + buffer); System.out.println(new String(buffer.array()));
Java一般用BufferedInputStream、BufferedReader等帶緩沖的I/O類來處理大檔案,但如果檔案超大的話,比如達到GB,甚至TB級別,更快的方式是采用NIO中引入的檔案記憶體映射方案:MappedByteBuffer,
你只需要MappedByteBuffer讀寫性能極高,最主要的原因就是因為它實作了對異步操作的支持,就可以了!
可以用大檔案來試一下:
// ByteBuffer讀取大檔案 public static void useFileChannel() { try{ FileInputStream fis = new FileInputStream("你電腦上已經存在的檔案路徑,例如C:\\file1"); FileChannel channel = fis.getChannel(); long start = System.currentTimeMillis(); ByteBuffer buff = ByteBuffer.allocate((int) channel.size()); buff.clear(); channel.read(buff); long end = System.currentTimeMillis(); System.out.println(end - start); fis.close(); channel.close(); } catch (IOException e) { e.printStackTrace(); } } // MappedByteBuffer讀取大檔案 public static void useMappedByteBuffer() { try{ FileInputStream fis = new FileInputStream("你電腦上已經存在的檔案路徑,例如C:\\file1"); FileChannel channel = fis.getChannel(); long start = System.currentTimeMillis(); MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); long end = System.currentTimeMillis(); System.out.println(end - start); fis.close(); channel.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { useFileChannel(); useMappedByteBuffer(); }
最后把這兩個方法放到main()里面試試看效果,
NIO中的Buffer說這么多已經足夠了,用代碼去感受會更直接,
感謝您的大駕光臨!咨詢技術、產品、運營和管理相關問題,請關注后留言,歡迎騷擾,不勝榮幸~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/518695.html
標籤:Java
