segment中文意思是段恨课,部分,在這里是緩存數(shù)據(jù)存放的地方和措,數(shù)據(jù)就存放在一個一個的segment中庄呈,一個segment最大可存儲8192個字節(jié)。所有的segment以雙向循環(huán)鏈表的數(shù)據(jù)結構組織在一起派阱。
Segment類的成員變量
SIZE=8192诬留; 一個segment可存儲的最大字節(jié)數(shù)
SHARE_MINIMUM=1024; segment可被共享應含有的最小字節(jié)數(shù)(共享是為了不執(zhí)行拷貝操作)
byte[] data; 存放數(shù)據(jù)的字節(jié)數(shù)組
int pos贫母;下一個可讀的字節(jié)的位置
int limit文兑;下一個可寫的字節(jié)的位置
boolean shared;標識該segment是否是共享的
boolean owner; 標識該segment是否是字節(jié)數(shù)組的所有者(所有者可以修改腺劣,不是所有者是只讀權限)
Segment next; 鏈表中當前segment的下一個Segment
Segment prev;鏈表中當前segment的上一個Segment
Segment提供了從鏈表中刪除绿贞,插入以及相鄰segment的組合,一個segment拆分成兩個segment的操作橘原。
Segment.pop()
從鏈表中刪除自身籍铁。
prev ? 當前segment ? next -----> prev ? next
當前segment需要pop,那么當前segment 的next結點的prev指針就需要指向當前segment的prev結點趾断,當前segment的prev元素的next指針就需要指向當前segment的next結點拒名。
public final @Nullable Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
}
Segment.push(Segment)
將參數(shù)知道的segment插入到當前segment的后面
當前segment ? next -----> 當前segment ?插入的segment ? next
插入的segment的prev指針指向當前segment,插入的segment的next指針指向當前segment的next指針芋酌,當前segment的next結點的prev指針指向插入的segment增显,當前segment的next指針指向插入的segment。
public final Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
Segment.split(int byteCount)
將一個segment拆分成兩個segment脐帝,參數(shù)指定拆分后第一個segemnt含有的未讀字節(jié)數(shù)同云。所以第一個segment包含的數(shù)據(jù)范圍[pos,pos+ byteCount),第二個segment包含的數(shù)據(jù)范圍[pos+byteCount,limit)。
拆分時堵腹,當byteCount大于1024時使用共享segment炸站,否則執(zhí)行拷貝。這是出于兩方面考慮疚顷,一是避免拷貝數(shù)據(jù)旱易,所以使用共享的segment;二是避免過短的共享segment,以免鏈表中含有太多過短的segement咒唆。為了平衡兩者,規(guī)定共享的segment長度需要大于1024释液。
public final Segment split(int byteCount) {
//參數(shù)檢查全释,byteCount必須大于0,并且byteCount不能大于可讀數(shù)據(jù)長度
if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
Segment prefix;
//當byteCount大于1024時使用共享segment误债,否則執(zhí)行拷貝
if (byteCount >= SHARE_MINIMUM) {
prefix = sharedCopy();//①
} else {
//從segment池中取出一個segment
prefix = SegmentPool.take();//②
System.arraycopy(data, pos, prefix.data, 0, byteCount);
}
prefix.limit = prefix.pos + byteCount;//設置prefix的limit指針(下一個可寫的位置)
pos += byteCount;//設置當前segment的可讀指針位置
prev.push(prefix);//將prefix segment插入segment鏈表
return prefix;
}
① sharedCopy()
final Segment sharedCopy() {
shared = true;//當前為分享模式
//創(chuàng)建一個分享的segment
return new Segment(data, pos, limit, true, false);
}
//Segment構造函數(shù)
Segment(byte[] data, int pos, int limit, boolean shared, boolean owner) {
this.data = data;
this.pos = pos;
this.limit = limit;
this.shared = shared;
this.owner = owner;
}
② SegmentPool.take()
SegmentPool是未使用segment的集合浸船,采用單向鏈表組織在一起。只有兩個方法 take 和recycle寝蹈。
/**
* 取出一個可用的segment
*/
static Segment take() {
synchronized (SegmentPool.class) {
//next指向下一個可用的segment
if (next != null) {
Segment result = next;
next = result.next;//next指針指向next的下一個結點
result.next = null; //返回的segment的next指針置為null
byteCount -= Segment.SIZE;
return result;
}
}
//池中一個可用的segment都沒有李命,new 一個返回
return new Segment();
}
/**
*回收一個segment
*/
static void recycle(Segment segment) {
//還在segment鏈中的segment不能回收
if (segment.next != null || segment.prev != null) throw new IllegalArgumentException();
if (segment.shared) return; //分享的segment不能回收
synchronized (SegmentPool.class) {
if (byteCount + Segment.SIZE > MAX_SIZE) return; //加入后超過池子大小的不能回收
byteCount += Segment.SIZE;
//將segment加入單鏈表頭部
segment.next = next;
segment.pos = segment.limit = 0;//重置讀寫指針
next = segment;
}
}
Segment.compact()
將當前segment合并到前面一個segment。
首先計算當前segment可讀字節(jié)數(shù)箫老,然后計算前面一個segment可寫的字節(jié)數(shù)封字,這里分兩種情況,1耍鬓,前面一個segment是共享的阔籽,則剩余可寫的字節(jié)數(shù)=SIZE-prev.limit;2前面一個segment不是共享的牲蜀,那么其實pos前面的數(shù)據(jù)已經(jīng)是已讀的笆制,可以重新寫入,那么剩余可寫的字節(jié)數(shù)=SIZE-prev.limit+prev.pos涣达。比較當前segment可讀字節(jié)數(shù)與前面一個剩余可寫字節(jié)數(shù)大小在辆,如果前者大于后者,則不可寫入度苔,否則就寫入匆篓。
public final void compact() {
if (prev == this) throw new IllegalStateException();
if (!prev.owner) return; // Cannot compact: prev isn't writable.
//當前segment可讀字節(jié)數(shù)
int byteCount = limit - pos;
//前面一個segment可寫字節(jié)數(shù)(注意:已讀的字節(jié)部分是可以重新寫的,這就是加上prev.pos的原因)
int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
if (byteCount > availableByteCount) return;
writeTo(prev, byteCount);
//當前segment從鏈表移除
pop();
//回收到segmentpool
SegmentPool.recycle(this);
}
//將當前segment的未讀數(shù)據(jù)長度是byteCount寫到參數(shù)sink表示的segment中
public final void writeTo(Segment sink, int byteCount) {
if (!sink.owner) throw new IllegalArgumentException();
if (sink.limit + byteCount > SIZE) {
// 需要先將sink中已經(jīng)讀過的數(shù)據(jù)移走
if (sink.shared) throw new IllegalArgumentException();
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
sink.limit -= sink.pos;
sink.pos = 0;
}
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
sink.limit += byteCount;
pos += byteCount;
}