ThreadLocal,即線程本地變量,解決線程局部變量統一定義問題,從名稱應該就能看出它是線程的本地變量,是用於線程隔離的 ,而非用於多線程數據共享。題外話,它雖然可以用於隔離線程(純後臺問題),但它不能用於隔離網絡請求即無法分別無狀態的http請求(前臺與後臺交互問題),至於怎麼區分,就是cookie/session/tocken技術的知識了。
一、threadLocal的使用
ThreadLocal類封裝了getMap()、Set()、Get()、Remove()4個核心方法
1.set()方法,通過set(value)的方式將value值存入到 本地變量中,如下3.圖;
.2.get()方法,直接通過get的方式將value值存入到 本地變量中值獲取出來,如下3.圖;
這裏需要說明一點,雖然threadLocal的存取方式跟集合有些像,但它本身並不是一個集合,更像是 一個基本變量如int之類,只是賦值取值的方式變了,也就是說一個threadLocal變量它本身只能存一個值,當你重新給它set值的時候,之前的值就不存在了,如果你需要在一個線程內存多個變量值的話,需要定義多個threadLocal變量,如3下圖。
3. getMap()(非公共方法不可直接使用,通過get()方法內部調用它),獲取每個子線程Thread持有自己的ThreadLocalMap實例(即如上述所說,如果你定義多個本地變量,可以從這裏全部獲取), 因此它們是不存在併發競爭的。可以理解爲每個線程有自己的變量副本。通俗的講,當你在同一個線程內定義了多個本地變量時,這些threadLocal變量會存到當前線程的threadLocalMap變量中,ThreadLocal可以通過該方法獲取所有本地線程變量,如下threadLocal1與threadLocal2爲同一個線程內的變量,則存儲在同一個ThreadLocalMap中,threadLocal內部獲取的時候通過ThreadLocalMap map = getMap(Thread.currentThread())獲取到 當前線程的所有本地變量,並通過遍歷map或傳入threadLocal變量名獲得,如內部獲取時 ThreadLocalMap.Entry e = map.getEntry(this);
ThreadLocal<String> threadLocal1=new ThreadLocal<>();
ThreadLocal<String> threadLocal2=new ThreadLocal<>();
String th1="ceshi1";
String th2="ceshi2";
threadLocal1.set(th1);
threadLocal1.get();
threadLocal2.set(th2);
threadLocal2.get();
以上是它的主要使用方法 ,至於ThreadLocal,到底是怎麼把線程進行隔離的,只需要解析 一下它的源碼即可。
源碼按順序主要涉及:
1,ThreadLocal類的set()方法
2,Thread類的threadLocalMap變量,
3,ThreadLocal類的get()方法,
ThreadLocal類的set()方法,如下所示,thread在調用set方法的時候獲取到當前線程,並通過threadLocal類的getMap()方法,獲取到當前線程的threadLocalMap變量,然後以當前threadLocal實例作爲key,將值存入到threadLocalMap中,threadLocalMap
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
threadLocalMap爲threadLocal的一個內部類,它也有一個set()方法,該方法的作用主要是將傳進來的threadLocal實例作爲key,傳進來的value作爲value存入到Entry[]數組中,如下:
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
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;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
最後通過threadLocal的get()方法獲取到當前現成的本地變量,如下:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();
}
該方法,跟set方法類似,同樣是先獲得當前線程。並通過getMap方法獲取到當前線程的ThreadLocalMap變量,並已當前threadLocal實例作爲key通過調用ThreadLocalMap的getEntry()方法(可以看到比對了key值,即比對了是否是同一個 threadLocal實例)獲取到內部的Entry
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss. This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
*
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
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);
}
以上既是ThreadLocal從設置值到取到值的過程,整體來講思路就是,將ThreadLocal 實例作爲key並將set進實例的值作爲value存入到當前線程中一個變量中(ThreadLocalMap),也就是誰的變量誰來保存,也就不會出現線程之間共享的問題了,取得時候再通過threadLocal的get方法從當前線程中再把它拿出來即可。