ThreadLocal的官方API解釋爲:java
「該類提供了線程局部 (thread-local) 變量。這些變量不一樣於它們的普通對應物,由於訪問某個變量(經過其 get 或 set 方法)的每一個線程都有本身的局部變量,它獨立於變量的初始化副本。ThreadLocal 實例一般是類中的 private static 字段,它們但願將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。」編程
大概的意思有兩點:數組
ThreadLocal一般用來共享數據,當你想在多個方法中使用某個變量,這個變量是當前線程的狀態,其它線程不依賴這個變量,你第一時間想到的就是把變量定義在方法內部,而後再方法之間傳遞參數來使用,這個方法能解決問題,可是有個煩人的地方就是,每一個方法都須要聲明形參,多處聲明,多處調用。影響代碼的美觀和維護。有沒有一種方法能將變量像private static形式來訪問呢?這樣在類的任何一處地方就都能使用。這個時候ThreadLocal大顯身手了。安全
import java.util.HashMap;多線程
import java.util.Map;併發
public class TreadLocalTest {ide
// static ThreadLocal<HashMap> threadLocal = new ThreadLocal<HashMap>(){源碼分析
// @Override性能
// protected HashMap initialValue() {this
// System.out.println(Thread.currentThread().getName()+」initialValue」);
// return new HashMap();
// }
// };
public static class T1 implements Runnable {
private final static Map map = new HashMap();
int id;
public T1(int id) {
this.id = id;
}
public void run() {
// Map map = threadLocal.get();
for (int i = 0; i < 20; i++) {
map.put(i, i + id * 100);
try {
Thread.sleep(100);
} catch (Exception ex) {
}
}
System.out.println(Thread.currentThread().getName()
+ 「# map.size()=」 + map.size() + 」 # 」 + map);
}
}
public static void main(String[] args) {
Thread[] runs = new Thread[15];
T1 t = new T1(1);
for (int i = 0; i < runs.length; i++) {
runs[i] = new Thread(t);
}
for (int i = 0; i < runs.length; i++) {
runs[i].start();
}
}
}
這段程序的本意是,啓動15個線程,線程向map中寫入20個整型值,而後輸出map。運行該程序,觀察結果,咱們會發現,map中壓根就不止20個元素,這說明程序產生了線程安全問題。
咱們都知道HashMap是非線程安全的,程序啓動了15個線程,他們共享了同一個map,15個線程都往map寫對象,這勢必引發線程安全問題。
咱們有兩種方法解決這個問題:
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();
}
getMap方法的源碼:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
該方法返回的是當前線程中的ThreadLocalMap實例。閱讀Thread的源碼咱們發現Thread中有以下變量聲明:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
咱們暫時能夠將ThreadLocalMap理解爲一個相似Map的這麼個類,以後再講解它。
get()方法的大體意思就是從當前線程中拿到ThreadLocalMap的實例threadLocals,若是threadLocals不爲空,那麼就以當前ThreadLocal實例爲KEY從threadLocals中拿到對應的VALUE。若是不爲空,那麼就調用setInitialValue()方法初始化threadLocals,最終返回的是initialValue()方法的返回值。下面是setInitialValue()方法的源碼
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;
}
咱們看到map.set(this, value);這句代碼將ThreadLocalMap的實例做爲KEY,將initialValue()的返回值做爲VALUE,set到了threadLocals中。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
咱們看到它比setInitialValue()方法就少了個return語句。這兩種方式都能達到初始化ThreadLocalMap實例的效果。
ThreadLocal類只有三個屬性,以下:
/*ThreadLocal的hash值,map用它來存儲值*/
private final int threadLocalHashCode = nextHashCode();
/*改類能以原子的方式更新int值,這裏主要是在產生新的ThreadLocal實例時用來產生一個新的hash值,map用該值來存儲對象*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/*該變量標識每次產生新的ThreadLocal實例時,hash值的增量*/
private static final int HASH_INCREMENT = 0x61c88647;
剩下的就是一些方法。最關鍵的地方就是ThreadLocal定義了一個靜態內部類ThreadLocalMap。咱們在下一章節再來分析這個類。從ThreadLocal的類結構,咱們能夠看到,實際上問題的關鍵先生是ThreadLocalMap,ThreadLocal只是提供了管理的功能,咱們也能夠說ThreadLocal只是代理了ThreadLocalMap而已。
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();
}
這個方法的主要功能就是講KEY-VALUE存儲到ThreadLocalMap中,這裏至少咱們看到KEY其實是key.threadLocalHashCode,ThreadLocalMap一樣維護着Entry數組,這個Entry咱們在下一節會講解。這裏涉及到了Hash衝突的處理,這裏並不會向HashMap同樣衝突了以鏈表的形式日後添加。若是對這個Hash衝突解決方案有興趣,能夠再進一步研究源碼。
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
這裏咱們看到Entry集成了WeakReference類,泛型聲明瞭ThreadLocal,即每個Entry對象都保留了對ThreadLocal實例的弱引用,之因此這麼幹的緣由是,線程在結束以後須要將ThreadLocal實例從map中remove調,以便回收內存空間。
首先,ThreadLocalMap並非爲了解決線程安全問題,而是提供了一種將實例綁定到當前線程的機制,相似於隔離的效果,實際上本身在方法中new出來變量也能達到相似的效果。ThreadLocalMap跟線程安全基本不搭邊,綁定上去的實例也不是多線程公用的,而是每一個線程new一份,這個實例確定不是共用的,若是共用了,那就會引起線程安全問題。ThreadLocalMap最大的用處就是用來把實例變量共享成全局變量,在程序的任何方法中均可以訪問到該實例變量而已。網上不少人說ThreadLocalMap是解決了線程安全問題,實際上是望文生義,二者不是同類問題。
轉載自併發編程網 – ifeve.com本文連接地址: ThreadLocal使用