java nio中的ByteBuffer擴(kuò)展問(wèn)題
前言
在jdk1.4之前對(duì)于輸入輸出只能使用InPutStream和outPutSream這類傳統(tǒng)io模型,在jdk1.4之后新增了nio,什么是nio?
nio是new input/output 的簡(jiǎn)稱,nio的效率要比傳統(tǒng)io效率高,主要原因就是nio利用了系統(tǒng)底層的零拷貝技術(shù)和多路復(fù)用技術(shù)。
NIO核心知識(shí)
NIO有三個(gè)核心概念
- 1、Channal通道
- 2、Buffer緩沖
- 3、Selector選擇器
以上三者之間的關(guān)系是,一個(gè)線程擁有一個(gè)selector選擇器,一個(gè)selector選擇器管理多個(gè)channel通道,每個(gè)channel通道具有一個(gè)Buffer緩沖。為了更好的理解nio這三者之間的關(guān)系。舉一個(gè)實(shí)際生活中遇到的例子。
公司一般每年都會(huì)提供免費(fèi)的體檢,一般都是和愛康國(guó)賓合作的,去體檢了幾次發(fā)現(xiàn)一個(gè)有趣的事情:
1、客戶很多、體檢項(xiàng)目也很多。但是客戶不知道自己應(yīng)該去哪個(gè)房間體檢。
2、體檢中心提供一個(gè)中轉(zhuǎn)中心,中轉(zhuǎn)中心四周都是體檢房間,所有的人在每項(xiàng)體檢后都要經(jīng)過(guò)這個(gè)中轉(zhuǎn)中心。
3、每個(gè)體檢者手里都要握著一個(gè)體檢單。體檢單上會(huì)有自己的體檢項(xiàng)目。
4、中轉(zhuǎn)中心有一個(gè)工作人員(引導(dǎo)員),每個(gè)體檢人員來(lái)到中轉(zhuǎn)中心,引導(dǎo)員會(huì)結(jié)合體檢房間的空閑情況和體檢者的體檢單來(lái)指定具體要去的體檢場(chǎng)所。
5、體檢速度快,不會(huì)產(chǎn)生太長(zhǎng)的隊(duì)伍,比較高效的方式。
那這個(gè)這里里面有幾個(gè)角色完全可以對(duì)應(yīng)nio中的三個(gè)概念,分別是,引導(dǎo)員=selector, 體檢者手上的體檢單=Buffer, 每個(gè)體檢者=channel;
ByteBuffer
ByteBuffer是Buffer的一個(gè)子類,實(shí)現(xiàn)方式類似byte[], 該類有個(gè)重要的概念就是指針,指針標(biāo)志的位置代表下次操作的位置。
ByteBuffer分為讀模式和寫模式,當(dāng)然了這種稱謂都是人們習(xí)慣性的稱呼,其實(shí)只是指針指向的區(qū)別,比如讀模式指針指向數(shù)組下標(biāo)為0的位置,寫模式指針指向數(shù)組最后一位。
在實(shí)際使用中一個(gè)byteBuffer可能需要被反復(fù)讀取多次,于是byteBuffer提供了mark()方法,mark方法標(biāo)記的下標(biāo)供reset方法使用。
如果當(dāng)前byteBuffer中存儲(chǔ)的是[a,b,c,d,e]這五個(gè)字符,讀取的時(shí)候讀取到c的時(shí)候調(diào)用了mark()方法添加了標(biāo)記,那么當(dāng)讀取到e時(shí),又想從標(biāo)記的地方重新讀取時(shí),只需要調(diào)用reset()即可將指針指向c的位置,ByteBuffer提供的方法雖然很好,但是在日常使用的時(shí)候還是不太方便,比如在b處打了標(biāo)記,當(dāng)讀取到c時(shí)又打了標(biāo)記,目的時(shí)要從c處重新讀取一遍,然后再?gòu)腷處重新讀取一遍,這是byteBuffer就顯得有些不太好使了。
筆者寫了一個(gè)擴(kuò)展byteBuffer的類
代碼如下:
package pers.cz.tools; import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.Arrays; /** * @program: Reids * @description: 擴(kuò)展ByteBuffer,提供多次標(biāo)記的功能。 * @author: Cheng Zhi * @create: 2023-04-13 20:23 **/ public class JefByteBuffer { /** * 保存所有的標(biāo)記點(diǎn) */ private int[] markPocket = new int[16]; ByteBuffer byteBuffer; private int index = 0; public JefByteBuffer(int capacity, boolean isUseLocalMem) { if (isUseLocalMem) { byteBuffer = ByteBuffer.allocateDirect(capacity); } else { byteBuffer = ByteBuffer.allocate(capacity); } } public JefByteBuffer(ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } /** * 切換模式 */ public Buffer flip() { return byteBuffer.flip(); } public char read() { return (char) byteBuffer.get(); } public void unread() { int position = byteBuffer.position(); byteBuffer.position(position - 1); } public byte get() { return byteBuffer.get(); } public void put(byte[] src) { byteBuffer.put(src); } public void put(byte bi) { byteBuffer.put(bi); } public void clear() { byteBuffer.clear(); } public boolean isEnd() { int current = byteBuffer.position(); int end = byteBuffer.limit(); if (current == end) { return true; } return false; } /** * 打標(biāo)記 */ public void mark() { int mark = byteBuffer.position(); ensureCapacityInternal(); markPocket[index] = mark; index ++; } /** * 去除標(biāo)記 */ public void unmark() { index --; markPocket[index] = 0; } /** * 重置 */ public void reset() { index --; if (index < 0) { return; } int mark = markPocket[index]; if (mark < 0) { return; } byteBuffer.position(mark); } /** * 為markPocket擴(kuò)容 */ private void ensureCapacityInternal() { int oldCapacity = markPocket.length; if (index >= oldCapacity) { int newCapacity = oldCapacity + (oldCapacity >> 1); // minCapacity is usually close to size, so this is a win: markPocket = Arrays.copyOf(markPocket, newCapacity); } } }
使用方式:
public static void test2() { JefByteBuffer jefByteBuffer = new JefByteBuffer(100); jefByteBuffer.put(new byte[] {'1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t'}); jefByteBuffer.flip(); for (int i=1; i<21; i++) { if (i == 1) { jefByteBuffer.mark(); } if (i == 3) { jefByteBuffer.mark(); } if (i == 5) { jefByteBuffer.mark(); } if (i == 7) { jefByteBuffer.mark(); } if (i == 9) { jefByteBuffer.mark(); } if (i == 10) { jefByteBuffer.unmark(); } if (i == 11) { //jefByteBuffer.unmark(); // 這里將回到i=7的標(biāo)記點(diǎn) jefByteBuffer.reset(); } /* if (i ==8) { jefByteBuffer.reset(); }*/ char b = (char) jefByteBuffer.read(); jefByteBuffer.unread(); System.out.println(b); } }
這樣在使用byteBuffer的時(shí)候就可以靈活的讀取,方法名更是見名知意,比如read讀取了一個(gè)字節(jié)之后,調(diào)用unread后還可以重新讀取。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java模擬實(shí)現(xiàn)HTTP服務(wù)器項(xiàng)目實(shí)戰(zhàn)
本文主要介紹了Java模擬實(shí)現(xiàn)HTTP服務(wù)器項(xiàng)目實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03@RequestBody注解Ajax post json List集合數(shù)據(jù)請(qǐng)求400/41
這篇文章主要介紹了@RequestBody注解Ajax post json List集合數(shù)據(jù)請(qǐng)求400/415的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10必知必會(huì)的SpringBoot實(shí)現(xiàn)熱部署兩種方式
這篇文章主要為大家介紹了必知必會(huì)的SpringBoot實(shí)現(xiàn)熱部署兩種方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04log4j中l(wèi)ogger標(biāo)簽中additivity屬性的用法說(shuō)明
這篇文章主要介紹了log4j中l(wèi)ogger標(biāo)簽中additivity屬性的用法說(shuō)明,基于很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Java多線程中的單例模式兩種實(shí)現(xiàn)方式
這篇文章主要介紹了Java多線程中的單例模式兩種實(shí)現(xiàn)方式的相關(guān)資料,需要的朋友可以參考下2017-04-04