ThreadLocal這個名臣帶有必定的迷惑性,千萬不要認爲ThreadLocal是線程的一種實現,網上不少人認爲它應該叫ThreadLocalVariable更貼切,我對此也很是贊同。ThreadLocal存在的意義就是爲了解決線程之間數據數據的衝突,ThreadLocal是線程的局部變量,它裏面的數據只存活在線程的聲明週期之中,並且必須是當前線程才能獲取到對應的數據,其餘的線程不能獲取到當前線程的ThreadLocal中的數據。html
在對ThreadLocal作了一個簡單的介紹後,下面開始逐一分析。java
1.ThreadLocal是什麼?算法
ThreadLocal的本質是線程局部變量,能夠理解爲ThreadLocalVariable,它並不是是線程的本地實現,不是一個Thread。多線程
ThreadLocal的源碼中類聲明以下:併發
public class ThreadLocal<T> {...
2.ThreadLocal的做用?this
ThreadLocal存在是爲了解決什麼問題的呢?在多線程併發的狀況下,各個線程的在其上下文環境下保存一些變量副本,spa
能夠獨立的改變本身的副本,而不會和其餘線程的副本衝突。線程
ThreadLocal的數據實際存儲在ThreadLocalMap中,而且以當前的ThreadLocal對象做爲key,當對ThreadLocal作get和set方法的時候,都是以this爲key進行操做的,所以其的ThreadLocal對象獲取當前的ThreadLocal對象的值。code
setorm
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
get
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(); }
3.ThreadLocal中的變量存儲在何處?
ThreadLocal中數據式存儲在ThreadLocal.ThreadLocalMap中的。這個ThreadLocalMap的對象是被Thread對象的threadLocals的屬性引用的。因而可知,ThreadLocal中變量存在Thread對象中,所以在線程的存活期間,若是沒有刻意的去刪除這些屬性,實際上在線程聲明週期中都可以對這些變量進行訪問和操做。
ThreadLocalMap是做爲Thread類的一個字段。
class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); } private char name[]; private int priority; private Thread threadQ; private long eetop; /* Whether or not to single_step this thread. */ private boolean single_step; /* Whether or not the thread is a daemon thread. */ private boolean daemon = false; /* JVM state */ private boolean stillborn = false; /* What will be run. */ private Runnable target; /* The group of this thread */ private ThreadGroup group; /* The context ClassLoader for this thread */ private ClassLoader contextClassLoader; /* The inherited AccessControlContext of this thread */ private AccessControlContext inheritedAccessControlContext; /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; } /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ //請看這裏---- ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap什麼時候建立,並將對象引用和Thread的threadLocals屬性關聯上呢?在ThreadLocal中有一個createMap的方法,在這個方法裏會建立一個ThreadLocalMap。請看下面代碼:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
此時尚未回答什麼時候回建立ThreadLocalMap這個問題。實際上在前面的set方法中已經看到調用createMap的方法了,說明在第一設置數據的時候,若是使用當前ThreadLocal對象沒有找到ThreadLocalMap對象的時候,那麼將會去建立ThreadLocalMap對象,可是起初沒有set就開始調用get方法呢?在get方法裏面調用一個叫setInitialValue的方法。事實上setInitialValue和set方法很是類似,在setInitialValue中也調用createMap方法。請看下面代碼:
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
至此,我能夠知道了ThreadLocalMap是存儲在Thread對象中,其次TheadLocal會在首次調用get或者set的時候建立ThreadLocalMap對象,而且將這個ThreadLocalMap的對象賦給當前線程的threadlocals屬性。
4.ThreadLocal中數據存儲詳細分析。
(1)ThreadLocal中數據是存儲在ThreadLocalMap的對象中。而且是ThreadLocalMap對象和Thread對象時一對一的關係。
(2)一個ThreadLocalMap對象對應的ThreadLocal對象是N個。
(3)ThreadLocalMap的Entry是一個WeakReference<ThreadLocal>子類,ThreadLocalMap中存儲的時候是以ThreadLocal的對象做爲key,當key釋放了引用(key==null),ThreadLocalMap會馬上移除對應Entry。
咱們再次看一下ThreadLocalMap.Entry的聲明:
static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
5.ThreadLocal回收。參考資料來自ThreadLocal內存泄露
被廢棄了的ThreadLocal所綁定對象的引用,會在如下4狀況被清理。
若是此時外部沒有綁定對象的引用,則該綁定對象就能被回收了:
1 Thread結束時。
2 當Thread的ThreadLocalMap的threshold超過最大值時。
3 向Thread的ThreadLocalMap中存放一個ThreadLocal,hash算法沒有命中既有Entry,而須要新建一個Entry時。
4 手工經過ThreadLocal的remove()方法或set(null)。
所以若是咱們粗暴的把ThreadLocal設置null,而不調用remove()方法或set(null),那麼就可能形成ThreadLocal綁定的對象長期也能被回收,於是產出內存泄露。