引導語
ThreadLocal也是線程安全的一種措施,有這麼一個經典的比喻“人手一隻筆”,如果有100個人要簽名,只有一支筆的話,那麼肯定要排起長隊了,但如果準備了100支筆,那就可以做到每人一支筆啦。
ThreadLocal的官方解釋:線程局部變量,是一個以ThreadLocal對象爲鍵,任意對象爲值得存儲結構,這個結構倍附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢綁定在這個線程上的一個值。(Java併發編程的藝術,P105)
讓我們結合源碼和示例,來理解ThreadLocal吧!
一、ThreadLocal源碼
在Java.lang包下
1、構造方法:只有一個無參構造
public ThreadLocal() {}
2、常用方法:set()
public void set(T value) {
Thread t = Thread.currentThread(); //獲得當前線程
ThreadLocalMap map = getMap(t); //獲得ThreadLocalMap
if (map != null)
map.set(this, value); //map的常規賦值操作
else
createMap(t, value);
}
- 緊接着,我們看這個getMap()方法做了什麼
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //根據當前線程,從當前線程中取出ThreadLocalMap
}
- 原來存儲着各種變量的map在Thread中!讓我們來到Thread類的源碼中看看:
ThreadLocal.ThreadLocalMap threadLocals = null;
- 回到ThreadLocal源碼,再看看createMap()做了什麼:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 總結:set方法調用時,首先獲取當前線程,再從當前線程中獲取ThreadLocalMap,如果這個map不爲空,則set值,如果這個map是空的,則在當前線程創建ThreadLocalMap,然後再set。
- 注意:ThreadLocalMap是屬於線程的,這個map的key是ThreadLocal對象,value就是它在當前線程中的取值。
- 上圖:圖片來自網絡,侵刪,謝謝!
3、常用方法:get()
public T get() {
Thread t = Thread.currentThread(); //先得到當前線程
ThreadLocalMap map = getMap(t); //取出當前線程中的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
三、使用示例
- ThreadLocal一般定義成靜態成員變量,以供使用
1、實現了Runnable接口的類
class ThreadLocalTest implements Runnable{
static ThreadLocal<String> str=new ThreadLocal<String>(){
@Override
protected String initialValue() {
return Thread.currentThread().getName(); //設置初始值爲線程名
}
};
@Override
public void run(){
try {
//打印出當前線程名,ThreadLocal對象str在當前線程中的值
System.out.println(Thread.currentThread().getName()+":"+str.get());
Thread.sleep(10);
}catch (Exception e){
System.out.println(e);
}
}
}
2、主方法
public class TryThreadLocal {
public static void main(String[] args){
Thread t1=new Thread(new ThreadLocalTest());
Thread t2=new Thread(new ThreadLocalTest());
t1.start();
t2.start();
}
}
3、運行結果
三、應用場景
1、用在數據庫連接中,用threadlocal保存Connection對象,可以保證在線程各處的數據庫連接都是相同的,而且各個請求之間不影響。
2、用在session管理中,可以保證每個請求的會話不會相互影響。
3、線程上下文相關的信息,例如用戶ID、交易ID等等。
感謝:
https://my.oschina.net/davidzhang/blog/111010
https://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/