爲解決多線程程序的併發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多線程程序,ThreadLocal並不是一個 Thread ,而是 Thread 的局部變量。
查看Thread 源碼可得知:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
ThreadLocal 原理:每個線程的內部都維護了一個 ThreadLocalMap,它是一個 Map(key,value)數據格式,key 是一個弱引用,也就是 ThreadLocal 本身,而 value 存的是線程變量的值。
直接運行一段代碼
/**
* 36ThreadLocal 使用及實現原理
*
Thread-0 1
Thread-1 1
Thread-2 1
Thread-0 2
Thread-1 2
Thread-2 2
Thread-1 3
Thread-0 3
Thread-2 3
* @author Administrator
*/
public class Demo38ThreadLocal {
private ThreadLocal<Integer> count = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return new Integer(0);
}
};
public int getNext() {
Integer value = count.get();
value++;
count.set(value);
return value;
}
public static void main(String[] args) {
Demo38ThreadLocal demo = new Demo38ThreadLocal();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() +" "+demo.getNext());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() +" "+ demo.getNext());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() +" "+ demo.getNext());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
線程資源持有
線程獨立資源持有是threadlocal最常用的使用方式,比如從單線程應用擴展到多線程應用。
線程資源一致性
Entry數組的長度爲啥一定要是2的冪
Hash 衝突
ThreadLocalMap 中解決 Hash 衝突的方式並非鏈表的方式,而是採用線性探測的方式.
所謂線性探測,就是根據初始 key 的 hashcode 值確定元素在 table 數組中的位置,如果發現這個位置上已經被其他的 key 值佔用,則利用固定的算法尋找一定步長的下個位置,依次判斷,直至找到能夠存放的位置。
ThreadLocal特性
ThreadLocal和Synchronized都是爲了解決多線程中相同變量的訪問衝突問題,不同的點是
- Synchronized是通過線程等待,犧牲時間來解決訪問衝突
- ThreadLocal是通過每個線程單獨一份存儲空間,犧牲空間來解決衝突,並且相比於Synchronized,ThreadLocal具有線程隔離的效果,只有在線程內才能獲取到對應的值,線程外則不能訪問到想要的值。
內存泄漏
ThreadLocal 應用場景
- 線程間數據隔離,各線程的 ThreadLocal 互不影響
- 方便同一個線程使用某一對象,避免不必要的參數傳遞
- 全鏈路追蹤中的 traceId 或者流程引擎中上下文的傳遞一般採用 ThreadLocal
- Spring 事務管理器採用了 ThreadLocal
- Spring MVC 的 RequestContextHolder 的實現使用了 ThreadLocal