1、簡介java
其實每一個線程內部都會維護一個ThreadLocalMap屬性,每份線程獨自的數據都存放在ThreadLocalMap中Entry[] table屬性裏,Entry對象的key就是ThreadLocal,value就是本身設置的值,若是程序裏有多個ThreadLoca屬性,每一個線程在運行時會將用到ThreadLocal,生成Entry保存到table中,有點須要注意的是同一個Entry中value從新設置會被替換,如Entry<ThreadLocal,value>中value被設置成value2,變成Entry<ThreadLocal,value2>,和Map相似,若是想保存多個值,能夠將value封裝成對象。數組
2、屬性bash
//ThreadLocal中的hash值,目的是爲了定位存放在ThreadLocalMap中Entry[] table的那個位置
private final int threadLocalHashCode = nextHashCode();
//原子類,對hashCode進行累加HASH_INCREMENT
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;複製代碼
3、內部類併發
一、SuppliedThreadLocalide
//目的是爲了在初始化調用ThreadLocal.get()方法時,能根據傳入的函數式接口supplier的get方法獲取默認值
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier)
{
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
return supplier.get();
}
}複製代碼
二、ThreadLocalMap函數
//每一個線程Thread,內部都維護了一個ThreadLocal.ThreadLocalMap threadLocals = null屬性,每一個線程的//數據都各自保存在各自的ThreadLocalMap的table屬性中,table中的每一個元素都是Entry<ThreadLocal,Value>,
//每一個ThreadLocal,都會做爲Entry的key,好比有個Test類,內部有兩個ThreadLocal,若是A線程在運行時,使用到兩個ThreadLocal,那線程A中的ThreadLocalMap中的table[Entry0<ThreadLocal,value>,Entry1<ThreadLocal1,value1>]
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//table初始容量
private static final int INITIAL_CAPACITY = 16;
//保存每一個線程中的使用到的ThreadLocal做爲key的多個Entry
private Entry[] table;
//table中的元素
private int size = 0;
//擴容的閾值
private int threshold; 複製代碼
4、構造ThreadLocal對象ui
//傳入函數式接口,構造ThreadLocal的靜態內部類子類SuppliedThreadLocal,ThreadLocal.get()還未設置值時獲取null,SuppliedThreadLocal.get()獲取的值是傳入的函數式接口supplier的get方法的值
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}複製代碼
public ThreadLocal() {}複製代碼
5、get方法this
public T get() {
//獲取當前執行的線程
Thread t = Thread.currentThread();
//從Thread中獲取threadLocals屬性,即Thread中的ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocalMap map = getMap(t);//看下面的getMap方法
//若是當前線程的ThreadLocalMap不爲null
if (map != null) {
//getEntry看下面介紹的ThreadLocalMap的getEntry方法
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();}複製代碼
getMap方法,從Thread中獲取threadLocal屬性spa
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}複製代碼
setInitialValue方法,初次構造ThreadLocalMap對象,並設置firstValue線程
private T setInitialValue() {
//若是是經過構造函數構造ThreadLocal,initialValue()方法返回null,若是是經過withInitial靜態方法構造ThreadLocal,initialValue()方法返回supplier.get();
T value = initialValue();
//獲取當前線程
Thread t = Thread.currentThread();
//從Thread中獲取threadLocal屬性
ThreadLocalMap map = getMap(t);
if (map != null)
//若是map不爲null,將其value設置進去,key爲當前的ThreadLocal,set會在下面set方法中介紹
map.set(this, value);
else
//構造ThreadLocalMap,而且將其ThreadLocalMap賦值給當前線程的threadLocals屬性
createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, firstValue);
return value;}複製代碼
ThreadLocalMap構造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//建立個大小爲16的Entry數組
table = new Entry[INITIAL_CAPACITY];
//根據ThreadLocal的threadLocalHashCode屬性值&上table數組長度-1,計算出該ThreadLocal的Entry的存放位置
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//將其Entry存入數組中,Entry繼承於弱應用,這樣的話當ThreadLocal被賦值爲null,gc能夠回收掉ThreadLocal,而不至於Entry對ThreadLocal的引用,致使gc回收不了,防止內存泄漏
table[i] = new Entry(firstKey, firstValue);
//table數組中存在的元素
size = 1;
//設置擴容的閾值大小 threshold = len * 2 / 3 table數組長度乘於2除以3
setThreshold(INITIAL_CAPACITY);
}複製代碼
//線程間的InheritableThreadLocal傳遞,這個會在詳細介紹InheritableThreadLocal中介紹
private ThreadLocalMap(ThreadLocalMap parentMap) {
//父線程的ThreadLocalMap
Entry[] parentTable = parentMap.table;
//長度
int len = parentTable.length;
//設置當前線程的閾值
setThreshold(len);
//建立大小和父線程相同的Entry數組
table = new Entry[len];
//將其父線程中的ThreadLocalMap中Entry和Entry的key(ThreadLocal)不爲空的Entry的元素添加到子線程的Entry的table中
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
//childValue()方法只有在ThreadLocalMap的子類InheritableThreadLocal中才能使用,不然在ThreadLocal中調用會報UnsupportedOperationException異常
Object value = key.childValue(e.value);//return parentValue
Entry c = new Entry(key, value);
//計算Entry的存放位置
int h = key.threadLocalHashCode & (len - 1);
//若是當前位置有存在,則存放下一個位置,只到下一個位置沒有元素
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
//當前線程的Entry的table數組的元素大小加1
size++;
}
}
}
}複製代碼
createInheritedMap方法,調用ThreadLocalMap的私有構造方法
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}複製代碼
createInheritedMap方法的在哪裏被調用
//Thread類中的init方法
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
//.....省略n行代碼,inheritThreadLocals默認爲true,若是建立當前線程的父線程inheritableThreadLocal不爲空,將其inheritableThreadLocals的ThreadLocalMap中的Entry的table數組賦值給當前線程 //好比在main線程中建立了test線程,而且main中的屬性inheritableThreadLocals不爲空,會將其inheritableThreadLocals中的的ThreadLocalMap中的Entry的table數組賦值給test線程,inheritableThreadLocals屬性的類只能是InheritableThreadLocal
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}複製代碼
ThreadLocalMap的getEntry方法
private Entry getEntry(ThreadLocal<?> key) {
//ThreadLocal的threadLocalHashCode & 上 ThreadLocalMap的Entry數組table的長度-1,定位元素所在的下標
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
//entry不等於null而且e.get和當前的ThreadLocal相等,返回entry
if (e != null && e.get() == key)
return e;
else
//若是entry等於null,獲取e.get()和當前的ThreadLocal不相等
return getEntryAfterMiss(key, i, e);}複製代碼
ThreadLocalMap的getEntryAfterMiss方法
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
//ThreadLocalMap的Entry數組table
Entry[] tab = table;
//數組長度
int len = tab.length;
//若是Entry e爲null,直接返回空,e不爲空,找到key和當前ThreadLocal相等的Entry,不然只到遍歷完table中的size
while (e != null) {
//獲取從entry中獲取key
ThreadLocal<?> k = e.get();
//若是key和當前的ThreadLocal相等,則直接返回entry
if (k == key)
return e;
//若是當前的entry的key爲null,即外部將其ThreadLocal的屬性賦值爲null,獲取被gc回收
if (k == null)
//清除當前key爲null的Entry,而且從新計算後面的每一個Entry元素的存放位置,說白了就是全部元素的位置都向前移動
expungeStaleEntry(i);
else
//獲取下一個ThreadLocal爲key的Entry
i = nextIndex(i, len);
e = tab[i];
}
return null;
}複製代碼
ThreadLocalMap的expungeStaleEntry方法
private int expungeStaleEntry(int staleSlot) { //當前線程的ThreadLocalMap的Entry數組table屬性
Entry[] tab = table;
//table數組的長度
int len = tab.length;
//將其當前key爲null的entry的value也設置爲null,加快value的gc回收
tab[staleSlot].value = null;
//將其整個entry設置爲null
tab[staleSlot] = null;
//table數組中存在的元素減1
size--;
Entry e;
int i;
//從當前key爲null的Entry的存放位置statleSlot位置開始日後面開始找到key爲null的entry而且清除掉,不然的話,從新計算key不爲null的Entry元素的存放位置
for (i = nextIndex(staleSlot, len); (e = tab[i]) != null;i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
//當前位置的entry的key爲null,清除value和entry,而且table數組元素減1
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
//從新計算當前位置的entry的存放位置
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
//將之前位置的entry置爲null
tab[i] = null;
//從新找到新的存放位置
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
//返回當前操做過的i
return i;
}
複製代碼
6、set方法
public void set(T value) {
//獲取當前線程
Thread t = Thread.currentThread();
//從當前線程中獲取ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//若是ThreadLocalMap不存在,構造ThreadLocalMap,而且將其ThreadLocalMap賦值給當前線程的threadLocals屬性 ,ThreadLocalMap的構造函數能夠看上面的介紹
createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, firstValue);
}複製代碼
ThreadLocalMap的set(this,value)方法
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//從當前算出來的位置i從後開始遍歷
for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//尋找到k和當前的ThreadLocal相等,替換值,爲此一個ThreadLocal只能存放線程的一個值
if (k == key) {
//將當前值設置到value中
e.value = value;
return;
}
//若是在遍歷的過程當中,存在某一項key爲null的Entry
if (k == null) {
//替換元素,而且作一些清除和從新計算元素的存放位置的操做,能夠看下面replaceStaleEntry方法的介紹
replaceStaleEntry(key, value, i);
return;
}
}
//當前位置i上本來就沒元素,或者在上面的遍歷中,沒有找到相同的key,或者某一項key的值爲null的entry替換,而是從遍歷中找到位置上沒有元素的i
tab[i] = new Entry(key, value);
//table的元素加1
int sz = ++size;
//若是沒有清除一些entry(key爲null),而且ThreadLocalMap中的元素個數大於閾值,則對table進行擴容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製代碼
replaceStaleEntry方法
private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
//將key爲null的entry的位置staleSlot賦值給新的變量
int slotToExpunge = staleSlot;
//從slotToExpunge位置向前遍歷,找到比此值更前的key爲null的entry的在table中的下標
for (int i = prevIndex(staleSlot, len);(e = tab[i]) != null;i = prevIndex(i, len))
//若是在遍歷前面table元素的時候找到key爲null的entry,對slotToExpunge的值進行從新賦值
if (e.get() == null)
slotToExpunge = i;
//從當前的staleSlot向後遍歷
for (int i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
//若是後面找到和當前ThreadLocal相等key的Entry
if (k == key) {
//賦予新值
e.value = value;
將參數傳進來的staleSlot(key爲null的entry下標)賦值給當前ThreadLocal的Entry的所在位置,說白了就是兩個互換位置
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
//若是前面沒有找到比當前staleSlot更前的key爲null的Entry位置,將當前i賦值給slotToExpunge
if (slotToExpunge == staleSlot)
slotToExpunge = i;
//cleanSomeSlots方法能夠看下面的cleanSomeSlots介紹,expungeStaleEntry方法能夠看上面expungeStaleEntry方法的介紹
cleanSomeSlots(expungeStaleEntry(slotToExpunge),len); return; }
//找到後面第一項key爲null的entry的下標i,賦值給slotToExpunge,目的是爲了後面執行expungeStaleEntry方法,清除key爲null的entry,而且從新移動後面元素的存儲位置
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
//將其當前傳進來key爲null的entry的value置空
tab[staleSlot].value = null;
//從新建立Entry對象存入當前位置
tab[staleSlot] = new Entry(key, value);
//若是slotToExpunge和staleSlot不相等,說明上面有找到其餘key爲null的entry
if (slotToExpunge != staleSlot)
//cleanSomeSlots和expungeStaleEntry配合使用,expungeStaleEntry清除後面key爲null的entry和對key不爲null的entry從新向前移動存儲位置,cleanSomeSlots是爲了清除前半部分key爲null的entry數據
cleanSomeSlots(expungeStaleEntry(slotToExpunge),len);}複製代碼
cleanSomeSlots方法
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
//獲取table中的下一個元素
i = nextIndex(i, len);
Entry e = tab[i];
//若是當前entry不爲null,key爲null
if (e != null && e.get() == null) {
n = len;
removed = true;
//調用上面介紹的expungeStaleEntry方法,尋找後面的key爲null的entry,將值和entry置爲空,而且對後面key不爲null的entry從新賦值到新位置
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}複製代碼
7、remove方法
public void remove() {
//從當前線程中獲取ThreadLocalMap
ThreadLocalMap m = getMap(Thread.currentThread());
//若是當前的ThreadLocalMap不爲null,調用下面的那個remove方法,說明若是當前線程沒有設置ThreadLocal.ThreadLocalMap threadLocals = null屬性,調用remove沒影響
if (m != null)
m.remove(this);
}
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//從當前位置開始日後找,找到key和當前ThreadLocal相等的Entry
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
//Entry元素的key設置爲null
e.clear();
//從當前位置開始查詢key爲null的entry,和從新計算後面存在的entry元素的位置,看上面expungeStaleEntry方法的介紹
expungeStaleEntry(i);
return;
}
}
}複製代碼
有不清楚的地方,能夠留言一塊兒探討