Java中ThreadLocal的使用及原理詳解
簡介
ThreadLocal是JDK提供的,提供線程本地變量,主要用來存放線程獨(dú)有變量和解決參數(shù)傳遞問題的。
例子
public static void main(String[] args) { ThreadLocal threadLocal = new ThreadLocal(); for(int i=0;i<3;i++){ new Thread(()->{ double random = Math.floor(Math.random()*10); threadLocal.set(random); System.out.println("設(shè)置線程"+Thread.currentThread().getName()+",線程變量:"+random); System.out.println("查看線程"+Thread.currentThread().getName()+",線程變量:"+threadLocal.get()); }).start(); } }
設(shè)置線程Thread-2,線程變量:4.0
設(shè)置線程Thread-0,線程變量:9.0
設(shè)置線程Thread-1,線程變量:5.0
查看線程Thread-0,線程變量:9.0
查看線程Thread-2,線程變量:4.0
查看線程Thread-1,線程變量:5.0
可以看出,每個線程的變量是隔離開的,避免了出現(xiàn)線程不安全的問題,ThreadLocal是如何實(shí)現(xiàn)的呢?
原理
ThreadLocalMap
public class Thread implements Runnable { ThreadLocal.ThreadLocalMap threadLocals = null; }
ThreadLocalMap是真正存儲數(shù)據(jù)的地方,ThreadLocalMap在各個線程中,為線程獨(dú)有的
set方法
public void set(T value) { //獲取調(diào)用者線程 Thread t = Thread.currentThread(); //獲取調(diào)用者線程自有的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) //如果map!=null直接設(shè)置值 map.set(this, value); else //創(chuàng)建ThreadLocalMap,并將value存儲 createMap(t, value); } ThreadLocalMap getMap(Thread t) { //獲取線程的ThreadLocalMap return t.threadLocals; } void createMap(Thread t, T firstValue) { //創(chuàng)建ThreadLocalMap,并設(shè)置firstValue t.threadLocals = new ThreadLocalMap(this, firstValue); }
從set方法中第二行代碼ThreadLocalMap map = getMap(t);這段代碼是獲取當(dāng)前線程的ThreadLocalMap,然后進(jìn)行設(shè)置值,通過getMap方法可以看出,線程本地變量并不是存儲在ThreadLocal,而是存儲在各自線程的ThreadLocalMap threadLocals中的,ThreadLocal只是相當(dāng)于一個工具類,對ThreadLocalMap進(jìn)行操作而已。
get方法
public T get() { //獲取調(diào)用者線程 Thread t = Thread.currentThread(); //獲取調(diào)用者線程自有的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { //獲取到當(dāng)前ThreadLocal的值 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } private T setInitialValue() { //獲取默認(rèn)值(null),也可以重寫該方法 T value = initialValue(); //獲取調(diào)用者線程 Thread t = Thread.currentThread(); //獲取調(diào)用者線程自有的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) //設(shè)置為空 map.set(this, value); else //創(chuàng)建一個ThreadLocalMap,賦值為空 createMap(t, value); return value; }
get方法是獲取當(dāng)前線程的ThreadLocalMap,然后通過將ThreadLocal當(dāng)做key,從ThreadLocalMap中獲取到相應(yīng)的Entry,Entry里包含了key和value,最后將value返回,如果沒有key的值,就會調(diào)用setInitialValue方法,setInitialValue方法會初始化一個默認(rèn)值,默認(rèn)值是null,也可以重寫initialValue,獲得自己想要的默認(rèn)值,如果沒有創(chuàng)建ThreadLocalMap,則創(chuàng)建,然后返回默認(rèn)值
remove方法
public void remove() { //獲取調(diào)用者線程自有的ThreadLocalMap ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) //刪除ThreadLocalMap中key為當(dāng)前ThreadLocal的數(shù)據(jù) m.remove(this); }
remove方法十分簡單,就是刪除ThreadLocalMap中key為當(dāng)前ThreadLocal的數(shù)據(jù)
總結(jié)
通過看源碼,可以得出
- ThreadLcoal只是一個用來操作ThreadLocalMap的一個工具類和充當(dāng)ThreadLocalMap的key。
- ThreadLocalMap是真正存儲數(shù)據(jù)的,但是ThreadLocalMap是存儲在每個線程中的。
- 每個線程都有各自的ThreadLocalMap,這也是實(shí)現(xiàn)線程隔離的根本原因。
到此這篇關(guān)于Java中ThreadLocal的使用及原理詳解的文章就介紹到這了,更多相關(guān)ThreadLocal的使用及原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java并發(fā)編程學(xué)習(xí)之Unsafe類與LockSupport類源碼詳析
這篇文章主要給大家介紹了關(guān)于Java并發(fā)編程學(xué)習(xí)之Unsafe類與LockSupport類源碼的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧2018-06-06SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購物車基本功能
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購物車基本功能的實(shí)現(xiàn)過程,感興趣的小伙伴們可以參考一下2016-06-06Java Swing組件JFileChooser用法實(shí)例分析
這篇文章主要介紹了Java Swing組件JFileChooser用法,結(jié)合實(shí)例形式分析了java Swing組件JFileChooser文件選擇器的功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-11-11淺談Springboot下引入mybatis遇到的坑點(diǎn)
這篇文章主要介紹了Springboot下引入mybatis遇到的坑點(diǎn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Springboot下RedisTemplate的兩種序列化方式實(shí)例詳解
這篇文章主要介紹了Springboot下RedisTemplate的兩種序列化方式,通過定義一個配置類,自定義RedisTemplate的序列化方式,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09