Threadlocal源碼分析以及其中WeakReference做用分析

今天在看Spring 3.x企業應用開發實戰,第九章 Spring的事務管理,9.2.2節ThreadLocal的接口方法時,書上有提到Threadlocal的簡單實現,我就去看了下JDK1.8的Threadlocal的源碼。發現實現方式與書中講的並不相同,同時在網上搜索了一下,發現有比較多的人理解錯了。java

先看一下容易誤導的解釋:在ThreadLocal類中有一個Map對象,這個Map以每一個Thread對象爲鍵,保存了這個線程對應局部變量值,對應的實現方式以下:數組

 
 
 
 
public class SimpleThreadLocal { private Map valueMap = Collections.synchronizedMap(new HashMap()); public void set(Object newValue) { valueMap.put(Thread.currentThread(), newValue);//①鍵爲線程對象,值爲本線程的變量副本 } public Object get() { Thread currentThread = Thread.currentThread(); Object o = valueMap.get(currentThread);//②返回本線程對應的變量 if (o == null && !valueMap.containsKey(currentThread)) {//③若是在Map中不存在,放到Map 中保存起來。 o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; }}

爲何不按照那種有誤的方法實現呢?
看起來彷佛是不一樣線程獲取了各自的值,可是這些值並無線程獨立。線程A能夠操做線程B對應的值。若是某個線程將保存這些值的Map置爲null了,那麼其餘線程也沒法訪問了。this

其實是怎樣的呢
咱們看ThreadLocal的get()方法源碼。能夠看到獲取Threadlocal中的值,是先經過當前線程的線程對象t,獲取t的ThreadlocalMap屬性對象,而後再以Threadlocal對象爲鍵,去獲取ThreadlocalMap中的值。spa

 
 
 
 
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//獲取線程對象的屬性 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this);//這裏的this是指Threadlocal對象,Threadlocal在類中一般以靜態屬性出現,因此多個線程的Threadlocal指向同一個對象。 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }ThreadLocalMap getMap(Thread t) { return t.threadLocals;}

同時咱們查看ThreadLocal源碼中定義的靜態類ThreadLocalMap,其實底層封裝的是一個Entry數組,獲取方式和普通的HashMap不太同樣,若是沒有命中,就直接經過線性搜索,由於ThreadLocalMap須要保存的Entry並不會太多。線程

 
 
 
 
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 void set(ThreadLocal<?> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } }

經過ThreadLocal,每一個線程保存自身的數據,不能訪問到其餘線程的數據。code

ThreadLocalMap的Entry使用ThreadLocal的WeakReference引用做爲Key值,當全部線程運行出ThreadLocal的做用域時,即沒有強引用ThreadLocal時,ThreadLocal就會被回收。對象

 
 
 
 
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; }}

不是Threadlocal爲每一個線程提供了獨立的變量,而是每一個線程本身帶了本身獨立的變量。接口

關於內存泄漏
關於ThreadLocalMap的內存泄漏:若是一個ThreadLocal的生命週期結束,即在ThreadLocal所處的類中沒有了強引用,而Thread沒有結束,在Thread的threadLocals成員變量中,會有一個Entry使用弱引用引用了ThreadLocal做爲key,由於是弱引用,這個key將被回收。而value是強引用,看起來是會形成泄漏,可是在ThreadLocalMap的set和get方法中,有一些釋放的方法。具體的我也不太懂。
仍是老老實實使用ThreadLocal的remove方法比較好。生命週期

 
 
 
 
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this);}


相關文章
相關標籤/搜索