核心思想:
每個線程存有一個map,這個map裡面數組存儲entry,是鍵threadlocal對應的value。意思一個線程對應多個鍵threadlocal對應多個value;(看源碼區分this和thread.currentthread,一個是位元組碼對象一個是目前線程對象)
核心元素:隻有一點
ThreadLocal.ThreadLocalMap
下面三行代碼來自Thread
ThreadLocal.ThreadLocalMap threadLocals = null;
一、執行個體化,且重寫T initialValue(),初始化首個變量值;
java.lang.ThreadLocal<Integer> ad = new java.lang.ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
那重寫後,哪裡調用?當首次get的時候會調用。
先擷取目前線程,把目前線程裡面的變量ThreadLocalMap 拿出來,
看看是否為空,不為空,就直接擷取this(threadlocal位元組碼對象)對應的值;
空,即把這個Thread裡面的變量ThreadLocalMap初始化, new一個數組(map思想,開放尋址);并把樓上初始化值設定進去(索引就是哈希位與數組長度);
put也就一樣的了;核心就是每個線程Thread裡面的ThreadLocalMap 變量;
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
擷取過程樓上代碼一層一層跟下去,沒什麼特别。為了解決記憶體溢出問題,jdk大神,把localThread弱引用,原因,如果不是弱引用,就算threadlocal自己null短了引用。但是Thread對threadlocalmap的引用還是在,因為不僅僅你一個threadlocal,接着map裡面還是存在。故弱引用。gc回收了treadlocal。key就變成了null。大神map在put、get等操作如果發現null就把髒資料給remove了。這個是亮點。有時間要研究下。而且你把keynull的去掉。那後面的用不用重新hash呢。因為之前這裡不為空,我把開放尋址的加1放到後面去了。他是不是要挪上來。麻煩
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// Rehash until we encounter null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
// Unlike Knuth 6.4 Algorithm R, we must scan until
// null because multiple entries could have been stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
執行個體:
多個線程公用一個對象(區域),想擁有同個變量的多個副本。且各個線程一個副本;
public static void main(String[] args) {
test.A a = new test().new A();
new Thread(new test().new Count("1",a)).start();
new Thread(new test().new Count("2",a)).start();
}
public class A{
java.lang.ThreadLocal<Integer> ad = new java.lang.ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
public void doSomething(){
ad.set(ad.get()+1);
}
}
public class Count implements java.lang.Runnable{
String threadName;
A a;
public Count(String threadName,A a){
this.threadName = threadName;
this.a = a;
}
@Override
public void run() {
while(true){
a.doSomething();
System.out.println(this.threadName+" "+a.ad.get());
}
}
}