???歡迎來到@邊境矢夢°的csdn博文??
???本文主要梳理線程數(shù)據(jù)共享和安全 -ThreadLocal??
??我是邊境矢夢°,一個正在為秋招和算法競賽做準(zhǔn)備的學(xué)生??
??喜歡的朋友可以關(guān)注一下??????,下次更新不迷路??
Ps: 月亮越亮說明知識點越重要 (重要性或者難度越大)??????????????
目錄
?? Java的有利武器:ThreadLocal?
?? 第一章 - 什么是ThreadLocal?
??第二章 - ThreadLocal原理
????源碼分析
?? 第三章 - 如何使用ThreadLocal
?? 第四章 - ThreadLocal的應(yīng)用場景
??總結(jié)
?? Java的有利武器:ThreadLocal ??
??今天我要為大家推薦一個Java中非常實用且神奇的工具——ThreadLocal。它可以讓我們在多線程環(huán)境下,輕松地實現(xiàn)線程私有的數(shù)據(jù)存儲。它可以幫助我們在多線程環(huán)境下輕松解決變量共享和線程安全的問題。??
?? 第一章 - 什么是ThreadLocal?
1. ThreadLocal 的作用,可以實現(xiàn)在同一個線程數(shù)據(jù)共享 , 從而解決多線程數(shù)據(jù)安全問題 .2. ThreadLocal 可以給當(dāng)前線程關(guān)聯(lián)一個數(shù)據(jù) ( 普通變量、對象、數(shù)組 )set 方法 [ 源碼 !]3. ThreadLocal 可以像 Map 一樣存取數(shù)據(jù), key 為當(dāng)前線程 , get 方法4. 每一個 ThreadLocal 對象,只能為當(dāng)前線程關(guān)聯(lián)一個數(shù)據(jù),如果要為當(dāng)前線程關(guān)聯(lián)多個數(shù) 據(jù),就需要使用多個 ThreadLocal 對象實例5. 每個 ThreadLocal 對象實例定義的時候,一般為 static 類型6. ThreadLocal 中保存數(shù)據(jù),在線程銷毀后,會自動釋放
?? 圖一:ThreadLocal示意圖
??第二章 - ThreadLocal原理
??一個ThreadLocal可以同時給多個線程分別關(guān)聯(lián)各自的數(shù)據(jù)。每個線程在訪問ThreadLocal時,都會獲取到自己獨立的數(shù)據(jù)副本,互不干擾。這就是ThreadLocal的獨立性和隔離性所表現(xiàn)出來的特點。
?? 圖二:ThreadLocal同時給多個線程分別關(guān)聯(lián)各自的數(shù)據(jù)示意圖
??ThreadLocal是用于實現(xiàn)線程局部變量的機制,它為每個線程提供了一個獨立的副本,保證了線程的隔離性。因此,一個ThreadLocal變量最多只能關(guān)聯(lián)一個線程的數(shù)據(jù)。
?? 圖三:ThreadLocal一個線程一個數(shù)據(jù)示意圖
????源碼分析
實際上,ThreadLocal本身并沒有存儲任何對象,所有的東西都存儲在Thread對象本身里!
首先,在Thread對象(注意,不是ThreadLocal!)里有個成員變量:
ThreadLocal.ThreadLocalMap threadLocals = null;
這個成員變量的類型是ThreadLocal.ThreadLocalMap,這是ThreadLocal的一個內(nèi)部類,可以理解為一個簡單的HashMap(關(guān)于這個內(nèi)部類的實現(xiàn)本篇就不詳細展開了,有興趣的同學(xué)可以看源碼),就是保存鍵值對的一個容器。
其實,所有set到ThreadLocal上的對象,實際都保存在當(dāng)前Thread中的threadLocals成員變量里。
為了說的清楚,下面我用偽代碼的形式,寫一下ThreadLocal的Set()方法的核心邏輯:
public void set(T value) {
Thread t = Thread.currentThread(); //先獲得當(dāng)前線程
if (t.threadLocals != null){ //判斷當(dāng)前線程上的threadLocals成員是否為空
t.threadLocals.set(this, value); //如果threadLocals不等于空,則set value,ThreadLocal對象作為key值。
}else{
t.threadLocals = new ThreadLocalMap(); //如果threadLocals為空,則創(chuàng)建之然后再set value。
t.threadLocals.set(this, value);
}
}
通過以上代碼大家應(yīng)該都能看清楚,真正的value值是保存在Thread對象上的,同時,key值是ThreadLocal對象。
再看看get方法,依然是只有核心邏輯的偽代碼:
public T get() {
Thread t = Thread.currentThread(); //先獲得當(dāng)前線程
if (t.threadLocals != null) { //判斷當(dāng)前線程上的threadLocals成員是否為空
return (T)t.threadLocals.get(this); //不為空,則以ThreadLocal對象作為key值得到value,然后返回。
}
return setInitialValue(); //返回初始值
}
理清了get和set方法,相信整個ThreadLocal的實現(xiàn)方式,大家應(yīng)該都比較清楚了。
?來自簡書.?作者:我愛紐約先生的一段話(鏈接在文末)
作者之所以這樣設(shè)計,我相信是經(jīng)過了充分考慮的。我能想到的一點,就是考慮了對象的生命周期。
ThreadLocal要為每個線程維護value值,所以它的生命周期一般是長于線程對象生命周期的。一個長生命周期的對象維護一個短生命周期對象的引用,就有可能內(nèi)存泄露,除非開發(fā)者要手動remove掉。
而把value存放在Thread上,一切就可以完美的解決。因為這個value對象是專門為Thread對象而存在的,所以value對象和Thread對象的生命周期應(yīng)該完全一致。把value對象存放在Thread對象里,則不會有內(nèi)存泄露的風(fēng)險。雖然,Thread會維護一個ThreadLocal的引用(實際上是個弱引用),但是如前所說,一個短生命周期的對象維護長生命周期對象的引用,一般沒什么問題。
?? 第三章 - 如何使用ThreadLocal
使用ThreadLocal非常簡單。我們只需創(chuàng)建一個ThreadLocal對象,并重寫initialValue()方法,定義每個線程初始的變量值。然后,通過調(diào)用get()方法可以獲取當(dāng)前線程的副本,而set()方法用于設(shè)置線程局部變量的值。
public class ThreadLocalExample {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
// 設(shè)置每個線程的初始值為0
return 0;
}
};
public static void main(String[] args) {
// 創(chuàng)建3個線程執(zhí)行任務(wù)
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(new MyRunnable());
t1.start();
t2.start();
t3.start();
try {
t1.join();
t2.join();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
int value = threadLocal.get();
System.out.println("線程:" + Thread.currentThread().getName() + ",初始值:" + value);
// 修改初始值
value += 5;
threadLocal.set(value);
// 再次獲取修改后的值
int updatedValue = threadLocal.get();
System.out.println("線程:" + Thread.currentThread().getName() + ",修改后的值:" + updatedValue);
}
}
}
?在上述代碼中,我們創(chuàng)建了一個名為threadLocal
的ThreadLocal對象,并重寫了它的initialValue()
方法,用于定義每個線程初始的變量值(這里設(shè)置為0)。
然后,我們創(chuàng)建了3個線程,并在每個線程的run()
方法中操作ThreadLocal對象。首先,通過get()
方法獲取當(dāng)前線程的副本,并輸出初始值。然后,我們使用set()
方法修改初始值,并再次使用get()
方法獲取修改后的值。
運行這段代碼,你會看到每個線程都有自己獨立的初始值和修改后的值。這表明每個線程通過ThreadLocal對象進行了數(shù)據(jù)隔離,互不干擾。
你可以根據(jù)自己的需求來定義ThreadLocal對象的值和操作邏輯。
?? 第四章 - ThreadLocal的應(yīng)用場景
ThreadLocal的應(yīng)用非常廣泛,下面為大家介紹幾個特別實用的場景:
1?? 數(shù)據(jù)庫連接管理:在多線程環(huán)境下,使用ThreadLocal可以方便地管理數(shù)據(jù)庫連接,每個線程都能獲取到自己獨立的連接副本,避免了線程安全和資源競爭問題。
2?? Web應(yīng)用用戶信息存儲:對于一個Web應(yīng)用程序,可以使用ThreadLocal將當(dāng)前用戶的登錄信息存儲在線程局部變量中,這樣在后續(xù)的請求處理過程中就無需傳遞這些信息了。
3?? 日志跟蹤:ThreadLocal還可以用于日志跟蹤,每個線程都有自己的日志副本,方便調(diào)試和定位問題。
??總結(jié)
??Thread里面存儲著各自的ThreadLocalMap, 并且Thread的每一個ThreadLocal會根據(jù)Thread的生命周期進行銷毀, 每個ThreadLocalMap都屬于某一個Thread, 而ThreadLocal只是ThreadLocalMap里面的一個而已, ThreadLocal對象實例就是Map中的鍵, 根據(jù)它就可以得到value, 一個ThreadLocal只能為一個Thread服務(wù)一個數(shù)據(jù), 但是同時可以為其他Thread服務(wù).
??為什么是一個ThreadLocal給Thread關(guān)聯(lián)一個數(shù)據(jù)呢?
因為set()方法中,?t.threadLocals.set(this, value); //如果threadLocals不等于空,則set value,ThreadLocal對象作為key值。
不管你怎么弄都是一個ThreadLocal對應(yīng)著一個相應(yīng)的Thread的
?? 圖四:ThreadLocal原理圖
?? 精彩的ThreadLocal世界就在眼前!無論你是面對高并發(fā)的系統(tǒng),還是只是想學(xué)習(xí)Java多線程編程,ThreadLocal都能幫助你輕松解決問題。希望這篇推薦能給你帶來靈感與啟發(fā)。如果你有其他關(guān)于ThreadLocal的經(jīng)驗和想法,也歡迎在評論區(qū)與我分享。下次再見!??
??????以下是對我?guī)椭芏嗟奈恼?文章來源:http://www.zghlxwxcb.cn/news/detail-696450.html
ThreadLocal到底是個什么東西? - 簡書 (jianshu.com)文章來源地址http://www.zghlxwxcb.cn/news/detail-696450.html
到了這里,關(guān)于【Java】線程數(shù)據(jù)共享和安全 -ThreadLocal的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!