1. Recycler工具類的使用
Recycler爲了避免我們重複的創建對象,使用對象池將我們使用過的數據保存起來,下一次就可以拿出來使用
public class TestRecycler {
// 對象池
private static final Recycler<User> RECYCLER = new Recycler<User>() {
@Override
protected User newObject(Handle<User> handle) {
return new User(handle);
}
};
// 用於測試的對象類
private static class User{
private final Recycler.Handle<User> handle;//handle負責對象的回收
public User(Recycler.Handle<User> handle){
this.handle = handle;
}
public void recycle(){
handle.recycle(this);
}
}
public static void main(String[] args) {
User user = RECYCLER.get();
user.recycle();
User user1 = RECYCLER.get();
// 重複利用user對象
System.out.println(user==user1);
}
}
執行結果爲true表明我們沒有new一個新的對象,第二次直接拿了原來的User對象來使用。
下面是Recycler的數據結構圖
2. Recycler的get()
方法
Recycler中維持了一個FastThreadLocal,裏面存放的是一個棧,戰中存儲數據的是DefaultHandle<?>[] elements
數組
public final T get() {
// 判斷線程池的容量等於0則直接返回一個Object
if (maxCapacityPerThread == 0) {
return newObject((Handle<T>) NOOP_HANDLE);
}
// fastThreadLocal中獲取一個stack
Stack<T> stack = threadLocal.get();
DefaultHandle<T> handle = stack.pop();
// 試圖從"池"中取一個handle,如果沒有成功就new一個handle
if (handle == null) {
handle = stack.newHandle();
handle.value = newObject(handle);
}
return (T) handle.value;
}
2.1 跟進pop()方法
DefaultHandle<T> pop() {
int size = this.size;// 獲取棧的長度
if (size == 0) {
if (!scavenge()) {// 當前線程創建的對象跑到其他線程中了(被其他線程回收了),使用scavenge()方法將這些數據拿回來
return null;
}
size = this.size;
if (size <= 0) {
// double check, avoid races
return null;
}
}
size --;
DefaultHandle ret = elements[size];// 數組尾部的數據拿出來
elements[size] = null;
// As we already set the element[size] to null we also need to store the updated size before we do
// any validation. Otherwise we may see a null value when later try to pop again without a new element
// added before.
this.size = size;
if (ret.lastRecycledId != ret.recycleId) {
throw new IllegalStateException("recycled multiple times");
}
ret.recycleId = 0;
ret.lastRecycledId = 0;
return ret;
}
3. Recycler的recycle()
方法
recycle()實現對對象的回收,主要就是將當前對象壓入棧中
// 默認的回收對象的實現方式
@Override
public void recycle(Object object) {
if (object != value) {
throw new IllegalArgumentException("object does not belong to handle");
}
Stack<?> stack = this.stack;
if (lastRecycledId != recycleId || stack == null) {
throw new IllegalStateException("recycled already");
}
stack.push(this);// 將對象再放入棧中
}
跟進push()
源碼
void push(DefaultHandle<?> item) {
Thread currentThread = Thread.currentThread();
if (threadRef.get() == currentThread) {
// The current Thread is the thread that belongs to the Stack, we can try to push the object now.
// 同步回收數據
pushNow(item);
} else {
// The current Thread is not the one that belongs to the Stack
// (or the Thread that belonged to the Stack was collected already), we need to signal that the push
// happens later.
pushLater(item, currentThread);
}
}
如果是當前線程創建的對象,會直接回收到當前線程的stack中,如果thread1創建的對象,倍thread2回收,就會被放到thread2的weekOrderQueue中
3.1 同步回收數據
指當前線程創建的對象被自己所回收。直接將其加入stack的DefaultHandle<?>[] elements
數組
private void pushNow(DefaultHandle<?> item) {
// 第一次回收時recycleId等於0,lastRecycledId等於0
if ((item.recycleId | item.lastRecycledId) != 0) {
throw new IllegalStateException("recycled already");
}
item.recycleId = item.lastRecycledId = OWN_THREAD_ID;// OWN_THREAD_ID本質是一個getAndIncrement()方法
int size = this.size;
// 大於回收上限,扔掉該對象
if (size >= maxCapacity || dropHandle(item)) {//dropHandle()方法保證回收對象是7/8
// Hit the maximum capacity or should drop - drop the possibly youngest object.
return;
}
// 超過數組的閾值了,進行擴容爲原來2倍
if (size == elements.length) {
elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));
}
elements[size] = item;
this.size = size + 1;
}
3.2 異步回收數據
指當前線程創建的對象被其他線程所回收,當thread1需要對象時,但是當前線程中的對象爲空了,就需要從其他線程中回收對象。
head
:指向第一個WeakOrderQueue
,也就是圖中的thread2
pre
:當前需要回收的thread的WeakOrderQueue
cursor
:pre的前一個節點
在之前Recycler中維持的stack的pop方法裏面scavenge()
就是異步回收數據。
跟蹤Scavenge()
方法
繼續跟蹤scavengeSome()
方法,這裏就是從其他線程中回收對象的核心邏輯。
private boolean scavengeSome() {
WeakOrderQueue prev;
WeakOrderQueue cursor = this.cursor;
if (cursor == null) {// 如果當前需要回收的線程的WeakOrderQueue爲null
prev = null;
cursor = head;//指針初始化
if (cursor == null) {
return false;// 返回false表示什麼都沒有回收到
}
} else {
prev = this.prev;
}
// 下面這個循環會不斷從cursor節點關聯的WeakOrderQueue中回收數據
boolean success = false;
do {
if (cursor.transfer(this)) {// 將cursor中的一個對象傳輸到當前的stack中
success = true;
break;
}
WeakOrderQueue next = cursor.getNext();// 回收失敗則指針後移一位,cursor指向下一個WeakOrderQueue
// cursor線程如果不存在了,做一些清理工作
if (cursor.get() == null) {
// If the thread associated with the queue is gone, unlink it, after
// performing a volatile read to confirm there is no data left to collect.
// We never unlink the first queue, as we don't want to synchronize on updating the head.
// 如果線程中有數據,則將數據傳輸到當前線程的stack中
if (cursor.hasFinalData()) {
for (;;) {
if (cursor.transfer(this)) {
success = true;
} else {
break;
}
}
}
// 將cursor節點中鏈表中移除
if (prev != null) {
// Ensure we reclaim all space before dropping the WeakOrderQueue to be GC'ed.
cursor.reclaimAllSpaceAndUnlink();
prev.setNext(next);
}
} else {
prev = cursor;
}
// cursor指針後移,繼續遍歷
cursor = next;
} while (cursor != null && !success);// 一直遍歷到鏈表尾部,結束循環
this.prev = prev;
this.cursor = cursor;
return success;
}
回顧一下,WeakOrderQueue的數據結構
我們繼續跟蹤一下transfer()
方法是如何從其他線程中取數據的
boolean transfer(Stack<?> dst) {
Link head = this.head.link;
// 頭節點爲空,表示當前的WeakOrederQueue中已經沒有數據了
if (head == null) {
return false;
}
// 如果當前head節點的DefaultHandler數組已經讀取到的下標等於link的容量
// 說明當前link中的數據已經被取走了
if (head.readIndex == LINK_CAPACITY) {
if (head.next == null) {
return false;
}
head = head.next;// head指向下一個link
this.head.relink(head);
}
final int srcStart = head.readIndex;// 當前link可以讀取的的下標起點
int srcEnd = head.get();//當前link目前的數據量個數表示讀取的數據終點
final int srcSize = srcEnd - srcStart;//算出可以回收的對象個數
if (srcSize == 0) {
return false;
}
final int dstSize = dst.size;//當前的stack的大小
//如果將當前link中的所有數據全部回收+stack中原來的數據個數就是傳輸結束後stack的容量
final int expectedCapacity = dstSize + srcSize;
// 預期的stack的容量大於satck的大小就對stack擴容
if (expectedCapacity > dst.elements.length) {
// actualCapacity表示擴容以後的stack的長度
final int actualCapacity = dst.increaseCapacity(expectedCapacity);
// 根據口容以後的容量大小,重新計算link讀取數據的結束下標
srcEnd = min(srcStart + actualCapacity - dstSize, srcEnd);
}
// 開始從當前的link中取數據到stack中
if (srcStart != srcEnd) {
final DefaultHandle[] srcElems = head.elements;//link
final DefaultHandle[] dstElems = dst.elements;//stack
int newDstSize = dstSize;
// 遍歷當前link完成數據回收到stack
for (int i = srcStart; i < srcEnd; i++) {
DefaultHandle<?> element = srcElems[i];
// 獲取當前的recycleId表示當前element未被回收,附上我們回收的lastRecycledId
if (element.recycleId == 0) {
element.recycleId = element.lastRecycledId;
} else if (element.recycleId != element.lastRecycledId) {//當前線程正在被其他線程回收
throw new IllegalStateException("recycled already");
}
srcElems[i] = null;
if (dst.dropHandle(element)) {//dropHandle控制回收的頻率
// Drop the object.
continue;
}
element.stack = dst;
dstElems[newDstSize ++] = element;
}
// 當前link被回收完了,且,還有下一個節點
if (srcEnd == LINK_CAPACITY && head.next != null) {
// Add capacity back as the Link is GCed.
this.head.relink(head.next);// 當前link可以再次接收LINK_CAPACITY個數據了,link的重複利用
}
head.readIndex = srcEnd;
// stack舊的長度和回收數據後的長度比較
// 相等表示沒有回收到數據
if (dst.size == newDstSize) {
return false;
}
dst.size = newDstSize;
return true;
} else {
// The destination stack is full already.
return false;
}
}