參考: http://www.cnblogs.com/dolphin0520/p/3920407.html 參考用例
https://www.cnblogs.com/ablejava/p/5914090.html 根本原因分析
1,正常使用
static final ThreadLocal<Long> longLocal = new ThreadLocal<>();
static final ThreadLocal<String> stringLocal = new ThreadLocal<>();
// 被調用的 入口
public void testThreadLocal(String threadNameA, String threadNameB) {
set();
new Thread(new Runnable() {
@Override
public void run() {
set();
get();
}
}, threadNameA).start();
new Thread(new Runnable() {
@Override
public void run() {
set();
get();
}
}, threadNameB).start();
get();
}
// 統一,放入資料
private void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
// 統一,擷取資料,并列印
private void get() {
long longNum = longLocal.get();
String strName = stringLocal.get();
LogUtil.v("longNum = " + longNum + ", strName = " + strName);
}
列印結果:
V/xxx->MainActivity.get(L:57):: LogUtil -> longNum = 56015, strName = newThreadA
V/xxx->MainActivity.get(L:57):: LogUtil -> longNum = 56016, strName = newThreadB
V/xxx->MainActivity.get(L:57):: LogUtil -> longNum = 1, strName = main
總結: * 以上測試代碼也是系統源碼中使用ThreadLocal的方式 * 以上測試結果,僅僅表明:ThreadLocal能夠避免線程安全問題,因為多線程操作并沒有産生沖突(多次嘗試列印結果一樣)
2,ThreadLocal的set、get方法
1) set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 擷取舊的ThreadLocalMap
if (map != null)
map.set(this, value); // 直接放置資料
else
createMap(t, value); // 建立新的ThreadLocalMap
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
set的作用為:若目前線程,無ThreadLocalMap,則建立并放置資料;若目前線程,有ThreadLocalMap,則繼續放入資料
2) get方法
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();
}
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;
}
2) get方法
get的作用為:若目前線程,有ThreadLocalMap,則擷取其中的資料;若目前線程,無ThreadLocalMap,則擷取預設初始化的資料 總結:
* ThreadLocal放置和擷取資料,都是以 this和value作為一對放入;是以,一個threadLocal,僅僅對應一個value
* ThreadLocal<T> 中的泛型,和value的資料類型一緻 * ThreadLocal每次存放資料,都是放置到ThreadLocalMap中,而ThreadLocalMap是屬于Thread的私有變量
3,ThreadLocalMap
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16; // 初始容量
private Entry[] table; // 具體存儲的資料
}
總結: * ThreadLocalMap和普通的map相同,是以數組作為實際的存儲對象
* ThreadLocalMap中數組對應的是,Entry(ThreadLocal、Value),并且key和value是弱引用;即:一旦某一方被回收,另一方是不會導緻記憶體洩漏的
4,整體記憶體分析
1) 記憶體是以Thread為主,Thread持有ThreadLocalMap、ThreadLocalMap存放着(ThreadLocal<Value> - Value)這樣的數組 是以,當多線程環境下,對應的引用關系如下圖所示:
結論:多線程環境下,ThreadLocal是靜态類型,是以多線程的key是相同的,但value是不同的;換句話說:value就是Thread的私有成員變量
5,依據以上分析解決常見的疑問
1) ThreadLocal 線程安全的原理 線程安全的核心在于:多線程在使用共享資料時,造成的沖突;是以,對于私有變量而言,自然而然就沒有線程安全的問題。 而value每次通過Thread擷取到的都是目前線程的私有變量,并沒有多線程共享該資料,當然沒有線程安全的問題
2) ThreadLocal 是否存在記憶體洩漏 ThreadLocalMap的存儲Entry{ThreadLocal - Value}關聯方式為弱引用,是以當某個ThreadLocal置空,gc時,Value是能夠被回收掉的;是以不存在記憶體洩漏問題
3) ThreadLocal 如何使用 * ThreadLocal一定是共享的,最好是static final類型,原因:ThreadLocal<T>,可以通過T來固定Value為單一類型;并且static final類型,能夠確定在記憶體中為一份且不可改變
* Value值一定不能是static類型,若Value為static類型,ThreadLocal線程安全将失效
4) ThreadLocal 使用空間換時間,如何了解 * 通過增加數組(存放引用)以及每個線程備份Value的方式,解決線程安全的問題
demo位址:
ThreadLocal 測試Activity
2) get方法