netty5.0原始碼解析 ByteBuf和相關輔助類
static final class Stack<T> implements Handle<T> { private static final int INITIAL_CAPACITY = 256; final Recycler<T> parent; final Thread thread; private T[] elements;//底層使用陣列實現儲存 private int size; private final Map<T, Boolean> map = new IdentityHashMap<T, Boolean>(INITIAL_CAPACITY); @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" }) Stack(Recycler<T> parent, Thread thread) { this.parent = parent; this.thread = thread; elements = newArray(INITIAL_CAPACITY); } @Override public void recycle(T object) { parent.recycle(object, this); } T pop() { int size = this.size; if (size == 0) { return null; } size --; T ret = elements[size]; elements[size] = null; map.remove(ret); this.size = size; return ret; } void push(T o) { if (map.put(o, Boolean.TRUE) != null) { throw new IllegalStateException("recycled already"); } int size = this.size; if (size == elements.length) { T[] newElements = newArray(size << 1); System.arraycopy(elements, 0, newElements, 0, size); elements = newElements; } elements[size] = o; this.size = size + 1; } @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" }) private static <T> T[] newArray(int length) { return (T[]) new Object[length]; } }
Recycler<T>中還定義了一個ThreadLocal<Stack<T>> threadLocal。ThreadLocal使得各執行緒能夠保持各自獨立的一個物件(每個執行緒對應一個例項)。通過ThreadLocal.set()將這個新建立的物件的引用儲存到各執行緒的自己的一個map中,每個執行緒都有這樣一個map,執行ThreadLocal.get()時,各執行緒從自己的map中取出放進去的物件或者呼叫initialValue()建立一個新物件,因此取出來的是各自執行緒中的物件,ThreadLocal例項是作為map的key來使用的。
ThreadLocal類介面很簡單,只有4個方法:
void set(T value)設定當前執行緒的執行緒區域性變數的值。
public T get()該方法返回當前執行緒所對應的執行緒區域性變數。
public void remove()將當前執行緒區域性變數的值刪除,目的是為了減少記憶體的佔用,該方法是JDK 5.0新增的方法。需要指出的是,當執行緒結束後,對應該執行緒的區域性變數將自動被垃圾回收,所以顯式呼叫該方法清除執行緒的區域性變數並不是必須的操作,但它可以加快記憶體回收的速度。
protected T initialValue()返回該執行緒區域性變數的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲呼叫方法,線上程第1次呼叫get()或set(T)時才執行,並且僅執行1次。ThreadLocal中的預設實現直接返回一個null。
關於ThreadLocal最後說一點,ThreadLocal不是用來解決物件共享訪問問題的,它僅僅只是讓各個執行緒都擁有了各自的物件。threadLocal本身僅僅只是作為一個key來幫助儲存目標物件,它跟目標物件沒有任何的聯絡僅僅只是些關於ThreadLocalMap操作方法。目標物件通過Thread中的ThreadLocalMap跟執行緒關聯。所以不需要考慮在threadLocal上面的同步問題,除非你線上程方法中去修改threadLocal自身的一些狀態,但這根本沒必要。所以將它設定為靜態變數就可以了免得每個執行緒都會有個threadLocal而浪費資源。多個執行緒共用一個threadLocal,但各個執行緒都擁有獨立的目標物件。