ThreadLocal的概念:摘自ThreaLocal的註釋java
This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * {@code get} or {@code set} method) has its own, independently initialized * copy of the variable. {@code ThreadLocal} instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID).
這段話的大概意思是ThreadLocal是保存的線程的本地變量,訪問get/set方法都是對線程獨立的。
大白話就是ThreadLocal是和線程相關的,在一個線程沒有結束以前,在任意方法中get/set在ThreadLocal中設置的值都是隻和當前線程有關。
所以呢,ThreadLocal的使用場景也能夠推測出來,能夠用來在一個線程中傳遞參數,或者某些狀況(好比session,數據庫操做句柄)只跟線程相關的時候來使用。數據庫
咱們接下來在看看源代碼中,ThreadLocal是什麼樣的?
簡答的貼了一下類和經常使用的幾個方法的源代碼(此博客是基於JDK1.8)
類定義:session
public class ThreadLocal<T>
從類定義上能夠看出ThreadLocal是支持泛型的ide
ThreadLocalMap:函數
static class ThreadLocalMap { 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,它裏面有一個靜態類Entry(能夠類比Map中的entry,保存實際的key和value,),ThreadLocalMap其實就是保存了ThreadLocal調用set方法設置的value,key就是ThreadLocal。
總結一下就是當咱們使用ThreadLocal的set方法時,ThreadLocal爲key,保存的泛型對象爲value,存到了ThreadLocal的內部類ThreadLocalMap中,而後ThreaLocalMap的鍵值對其實是放在靜態類Entry裏面。這裏稍微提一句
Entry是繼承了WeakReference弱引用,key是被弱引用的構造函數給建立的,value是強引用。(java的四種引用能夠參考我以前寫的文章Java四種引用簡介)源碼分析
get:post
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
get方法能夠看出,會先獲取當前線程,而後獲取ThreadLocalMap,而後把值從ThreadLocalMap中取出來,若是沒有ThreadLocalMap就去調用setInitialValue()設置完初始值,並返回。
set:this
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
set方法根據當前的線程從ThreadLocalMap中取得,沒有map就建立一個。thread中的 ThreadLocal.ThreadLocalMap threadLocals變量會指向剛纔建立的ThreadLocalMap。線程
1.初始化值
這樣看下來,應該對ThreadLocal的實現和原理有了一個大概的認識。這裏提一句,ThreadLocal直接new出來,而後去get的值是null。某些狀況下全部的線程都須要有一個初始化的值,這時候能夠重寫 protected T initialValue()方法,以下:code
private static ThreadLocal<String> threadLocal2 = new ThreadLocal<String>() { @Override protected String initialValue() { return "override initialValue的初始值"; } };
或者jdk1.8能夠使用ThreadLocal.withInitial初始化
private static ThreadLocal<String> threadLocal1 = ThreadLocal.withInitial(() -> "withInitial的初始值");
這樣全部的線程使用ThreadLocal的get都會是相同的一個初始化值。
2.多個線程有能夠相同的值
能夠使用InheritableThreadLocal,父線程中設置的值,子線程中能夠訪問到。這個用法和上面差很少就不舉例子了,有興趣的能夠本身研究下~
1.上面看ThreadLocalMap的時候,我們知道key是弱引用,gc的時候key會被回收,可是value和ThreadLocalMap的引用不會被回收。若是這種狀況的Thread不少,並且一直沒有執行完,就可能會出現內存泄漏。
2.在使用線程池的時候,當使用ThreadLocal調用set方法後,而後沒有調用remove的話,由於線程池的線程是複用的,若是同一個線程再去調用get方法可能拿到的值並非當時set進去的,致使程序數據異常之類的。儘可能用完值後就remove掉。
1.ThreadLocal在同一個線程傳值,或者只跟線程相關的場景使用
2.初始化ThreadLocal的值能夠用ThreadLocal.withInitial,或重寫initialValue()
3.父子線程共享相同的值使用InheritableThreadLocal
4.無論是正常使用仍是線程池使用ThreadLocal都必定要使用完就remove,不然會內存泄漏或者數據出錯