與Synchonized的對照: ThreadLocal和Synchonized都用於解決多線程併發訪問。但是ThreadLocal與synchronized有本質的差異。synchronized是利用鎖的機制,使變量或代碼塊在某一時該僅僅能被一個線程訪問。而ThreadLocal爲每一個線程都提供了變量的副本,使得每一個線程在某一時間訪問到的並不是同一個對象,這樣就隔離了多個線程對數據的數據共享。而Synchronized卻正好相反,它用於在多個線程間通訊時能夠得到數據共享。
Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。數據庫
用法數組
把要線程隔離的數據放進ThreadLocaltomcat
1static ThreadLocal<T> threadLocal = new ThreadLocal<T>() { 2 protected T initialValue() { 3 這裏通常new一個對象返回 4 } 5}
線程獲取相關數據的時候只要服務器
1threadLocal.get();
想修改、賦值只要多線程
1threadLocal.set(val)
使用場景架構
如上面說到的,ThreadLocal是用於線程間的數據隔離,ThreadLocal爲每一個線程都提供了變量的副本。併發
原理分析this
一、get()方法線程
1public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) {//當map已存在 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) { 7 @SuppressWarnings("unchecked") 8 T result = (T)e.value; 9 return result; 10 } 11 } 12 return setInitialValue();//初始化值 13} 14 15ThreadLocalMap getMap(Thread t) { 16 return t.threadLocals; 17}
上面先取到當前線程,而後調用getMap方法獲取對應的ThreadLocalMap,ThreadLocalMap是ThreadLocal的靜態內部類,而後Thread類中有一個這樣類型成員,因此getMap是直接返回Thread的成員
1ThreadLocal.ThreadLocalMap threadLocals = null; 來看下ThreadLocal的內部類ThreadLocalMap源碼,留個大體印象對象
1static class ThreadLocalMap { 2 private static final int INITIAL_CAPACITY = 16;//初始數組大小 3 private Entry[] table;//每一個能夠擁有多個ThreadLocal 4 private int size = 0; 5 private int threshold;//擴容閥值 6 static class Entry extends WeakReference<ThreadLocal<?>> { 7 Object value; 8 9 Entry(ThreadLocal<?> k, Object v) { 10 super(k); 11 value = v; 12 } 13 } 14 15 private Entry getEntry(ThreadLocal<?> key) { 16 int i = key.threadLocalHashCode & (table.length - 1); 17 Entry e = table[i]; 18 if (e != null && e.get() == key) 19 return e; 20 else 21 return getEntryAfterMiss(key, i, e); 22 } 23 24 private void set(ThreadLocal<?> key, Object value) { 25 Entry[] tab = table; 26 int len = tab.length; 27 int i = key.threadLocalHashCode & (len-1); 28 for (Entry e = tab[i]; 29 e != null; 30 e = tab[i = nextIndex(i, len)]) { 31 ThreadLocal<?> k = e.get(); 32 if (k == key) { 33 e.value = value; 34 return; 35 } 36 if (k == null) { 37 //循環利用key過時的Entry 38 replaceStaleEntry(key, value, i); 39 return; 40 } 41 } 42 tab[i] = new Entry(key, value); 43 int sz = ++size; 44 if (!cleanSomeSlots(i, sz) && sz >= threshold) 45 rehash(); 46 } 47 ... 48}
能夠看到有個Entry內部靜態類,它繼承了WeakReference,總之它記錄了兩個信息,一個是ThreadLocal<?>類型,一個是Object類型的值。getEntry方法則是獲取某個ThreadLocal對應的值,set方法就是更新或賦值相應的ThreadLocal對應的值。裏面涉及到擴容策略、Entry哈希衝突、循環利用等等再也不深刻,留個大體印象就好。
回顧下get()方法中的代碼
1if (map != null) { 2 ThreadLocalMap.Entry e = map.getEntry(this); 3 if (e != null) { 4 @SuppressWarnings("unchecked") 5 T result = (T)e.value; 6 return result; 7 } 8} 9return setInitialValue();
map爲null或e爲null就會走到setInitialValue,若是咱們是第一次get()方法,那map會是空的,因此接下來先看setInitialValue()方法
1private T setInitialValue() { 2 //調用咱們實現的方法獲得須要線程隔離的值 3 T value = initialValue(); 4 Thread t = Thread.currentThread(); 5 //拿到相應線程的ThreadLocalMap成員變量 6 ThreadLocalMap map = getMap(t); 7 if (map != null) 8 map.set(this, value); 9 else 10 createMap(t, value); 11 return value; 12}
上面initialValue就是實例化ThreadLocal要實現的方法,這裏又取了線程的ThreadLocalMap,不爲空就把值set進去(鍵爲TreadLocal自己,值就是initialValue返回的值);爲空就建立一個map同時添加一個值進去,最後返回value。
map.set(this, value)這句代碼在上面的ThreadLocalMap源碼中能夠看到大體流程,下面看看createMap()作了什麼事
1void createMap(Thread t, T firstValue) { 2 t.threadLocals = new ThreadLocalMap(this, firstValue); 3} 4 5 6ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 7 table = new Entry[INITIAL_CAPACITY]; 8 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 9 //建立一個Entry,加入數組 10 table[i] = new Entry(firstKey, firstValue); 11 size = 1; 12 setThreshold(INITIAL_CAPACITY); 13}
能夠看到在new ThreadLocalMap以後,就會建立一個Entry加入到數組中,最後把ThreadLocalMap的引用賦值給Thread的threadLocals成員變量
在回顧下get()方法中的代碼
1if (map != null) { 2 ThreadLocalMap.Entry e = map.getEntry(this); 3 if (e != null) { 4 @SuppressWarnings("unchecked") 5 T result = (T)e.value; 6 return result; 7 } 8} 9return setInitialValue();
如今map不會爲空了,再次調用get方法就會調用map的getEntry方法(上面的ThreadLocalMap源碼中能夠看到大體流程),拿到相應的Entry,而後就能夠拿到相應的值返回出去
二、set方法
分析完get()方法,那麼set()方法就天然而然的明白了,就再也不贅述
1public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); 6 else 7 createMap(t, value); 8}
總結
ThreadLocal的實現原理是,在每一個線程中維護一個Map,鍵是ThreadLocal類型,值是Object類型。當想獲取ThreadLocal的值時,就從當前線程中拿出Map,而後在把ThreadLocal自己做爲鍵從Map中拿出值返回。
提供線程內的局部變量。每一個線程都本身管理本身的局部變量,互不影響
內存泄漏問題。能夠看到ThreadLocalMap中的Entry是繼承WeakReference的,其中ThreadLocal是以弱引用形式存在Entry中,若是ThreadLocal在外部沒有被強引用,那麼垃圾回收的時候就會被回收掉,又由於Entry中的value是強引用,就會出現內存泄漏。雖然ThreadLocal源碼中的會對這種狀況進行了處理,但仍是建議不須要用TreadLocal的時候,手動調remove方法。
架構技術進階資料
+q q-q u n:948 368 769(免費獲取如下資料)
+q q-q u n:948 368 769(免費獲取如下資料)