java nio中的ByteBuffer擴展問題
前言
在jdk1.4之前對于輸入輸出只能使用InPutStream和outPutSream這類傳統(tǒng)io模型,在jdk1.4之后新增了nio,什么是nio?
nio是new input/output 的簡稱,nio的效率要比傳統(tǒng)io效率高,主要原因就是nio利用了系統(tǒng)底層的零拷貝技術(shù)和多路復(fù)用技術(shù)。
NIO核心知識
NIO有三個核心概念
- 1、Channal通道
- 2、Buffer緩沖
- 3、Selector選擇器
以上三者之間的關(guān)系是,一個線程擁有一個selector選擇器,一個selector選擇器管理多個channel通道,每個channel通道具有一個Buffer緩沖。為了更好的理解nio這三者之間的關(guān)系。舉一個實際生活中遇到的例子。
公司一般每年都會提供免費的體檢,一般都是和愛康國賓合作的,去體檢了幾次發(fā)現(xiàn)一個有趣的事情:
1、客戶很多、體檢項目也很多。但是客戶不知道自己應(yīng)該去哪個房間體檢。
2、體檢中心提供一個中轉(zhuǎn)中心,中轉(zhuǎn)中心四周都是體檢房間,所有的人在每項體檢后都要經(jīng)過這個中轉(zhuǎn)中心。
3、每個體檢者手里都要握著一個體檢單。體檢單上會有自己的體檢項目。
4、中轉(zhuǎn)中心有一個工作人員(引導(dǎo)員),每個體檢人員來到中轉(zhuǎn)中心,引導(dǎo)員會結(jié)合體檢房間的空閑情況和體檢者的體檢單來指定具體要去的體檢場所。
5、體檢速度快,不會產(chǎn)生太長的隊伍,比較高效的方式。
那這個這里里面有幾個角色完全可以對應(yīng)nio中的三個概念,分別是,引導(dǎo)員=selector, 體檢者手上的體檢單=Buffer, 每個體檢者=channel;
ByteBuffer
ByteBuffer是Buffer的一個子類,實現(xiàn)方式類似byte[], 該類有個重要的概念就是指針,指針標(biāo)志的位置代表下次操作的位置。
ByteBuffer分為讀模式和寫模式,當(dāng)然了這種稱謂都是人們習(xí)慣性的稱呼,其實只是指針指向的區(qū)別,比如讀模式指針指向數(shù)組下標(biāo)為0的位置,寫模式指針指向數(shù)組最后一位。
在實際使用中一個byteBuffer可能需要被反復(fù)讀取多次,于是byteBuffer提供了mark()方法,mark方法標(biāo)記的下標(biāo)供reset方法使用。
如果當(dāng)前byteBuffer中存儲的是[a,b,c,d,e]這五個字符,讀取的時候讀取到c的時候調(diào)用了mark()方法添加了標(biāo)記,那么當(dāng)讀取到e時,又想從標(biāo)記的地方重新讀取時,只需要調(diào)用reset()即可將指針指向c的位置,ByteBuffer提供的方法雖然很好,但是在日常使用的時候還是不太方便,比如在b處打了標(biāo)記,當(dāng)讀取到c時又打了標(biāo)記,目的時要從c處重新讀取一遍,然后再從b處重新讀取一遍,這是byteBuffer就顯得有些不太好使了。
筆者寫了一個擴展byteBuffer的類
代碼如下:
package pers.cz.tools;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* @program: Reids
* @description: 擴展ByteBuffer,提供多次標(biāo)記的功能。
* @author: Cheng Zhi
* @create: 2023-04-13 20:23
**/
public class JefByteBuffer {
/**
* 保存所有的標(biāo)記點
*/
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擴容
*/
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)記點
jefByteBuffer.reset();
}
/* if (i ==8) {
jefByteBuffer.reset();
}*/
char b = (char) jefByteBuffer.read();
jefByteBuffer.unread();
System.out.println(b);
}
}這樣在使用byteBuffer的時候就可以靈活的讀取,方法名更是見名知意,比如read讀取了一個字節(jié)之后,調(diào)用unread后還可以重新讀取。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot?Loki安裝簡介及實戰(zhàn)思路
這篇文章主要為大家介紹了SpringBoot?Loki安裝簡介及實戰(zhàn)思路詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪的相關(guān)資料2022-11-11
Java設(shè)計模式單例模式(Singleton)用法解析
這篇文章主要介紹了Java設(shè)計模式單例模式(Singleton)用法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
解析SpringBoot整合SpringDataRedis的過程
這篇文章主要介紹了SpringBoot整合SpringDataRedis的過程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06
spring @Profiles和@PropertySource實現(xiàn)根據(jù)環(huán)境切換配置文件
這篇文章主要介紹了spring @Profiles和@PropertySource根據(jù)環(huán)境切換配置文件,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

