Java ThreadLocal類別如何使用
如圖:
#快速開始
##接下來我們就先用一個簡單的範例給大家展示一下ThreadLocal的基本用法
package cuit.pymjl.thradlocal; /** * @author Pymjl * @version 1.0 * @date 2022/7/1 10:56 **/ public class MainTest { static ThreadLocal<String> threadLocal = new ThreadLocal<>(); static void print(String str) { //打印当前线程中本地内存中本地变量的值 System.out.println(str + " :" + threadLocal.get()); //清除本地内存中的本地变量 threadLocal.remove(); } public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread1 local variable"); //调用打印方法 print("thread1"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { //设置线程1中本地变量的值 threadLocal.set("thread2 local variable"); //调用打印方法 print("thread2"); //打印本地变量 System.out.println("after remove : " + threadLocal.get()); } }); t1.start(); t2.start(); } }

ThreadLocal的原理
ThreadLocal相關類別圖set
public void set(T value) { // 1.获取当前线程(调用者线程) Thread t = Thread.currentThread(); // 2.以当前线程作为key值,去查找对应的线程变量,找到对应的map ThreadLocalMap map = getMap(t); if (map != null) { // 3.如果map不为null,则直接添加元素 map.set(this, value); } else { // 4.否则就先创建map,再添加元素 createMap(t, value); } }
void createMap(Thread t, T firstValue) { /** * 这里是创建一个ThreadLocalMap,以当前调用线程的实例对象为key,初始值为value * 然后放入当前线程的Therad.threadLocals属性里面 */ t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap getMap(Thread t) { //这里就是直接获取调用线程的成员属性threadlocals return t.threadLocals; }
public T get() { // 1.获取当前线程 Thread t = Thread.currentThread(); // 2.获取当前线程的threadlocals,即ThreadLocalMap ThreadLocalMap map = getMap(t); // 3.如果map不为null,则直接返回对应的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 4.否则,则进行初始化 return setInitialValue(); }
下面是
setInitialValue的程式碼private T setInitialValue() {
//初始化属性,其实就是null
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//通过当前线程获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果map不为null,则直接添加元素
if (map != null) {
map.set(this, value);
} else {
//否则就创建,然后将创建好的map放入当前线程的属性threadlocals
createMap(t, value);
}
//将当前ThreadLocal实例注册进TerminatingThreadLocal类里面
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}
登入後複製
這裡我需要補充說明一下private T setInitialValue() { //初始化属性,其实就是null T value = initialValue(); //获取当前线程 Thread t = Thread.currentThread(); //通过当前线程获取ThreadLocalMap ThreadLocalMap map = getMap(t); //如果map不为null,则直接添加元素 if (map != null) { map.set(this, value); } else { //否则就创建,然后将创建好的map放入当前线程的属性threadlocals createMap(t, value); } //将当前ThreadLocal实例注册进TerminatingThreadLocal类里面 if (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; }
TerminatingThreadLocal
。這個類是jdk11新出的,jdk8中並沒有這個類,所以在網上很多源碼分析中並未看見這個類的相關說明。這個類別我看了一下原始碼,其作用應該是避免ThreadLocal記憶體洩漏的問題(有興趣的可以去看看源碼,若有錯誤,還請指正)。這是官方對其的解釋:/** * A thread-local variable that is notified when a thread terminates and * it has been initialized in the terminating thread (even if it was * initialized with a null value). * 一个线程局部变量, * 当一个线程终止并且它已经在终止线程中被初始化时被通知(即使它被初始化为一个空值)。 */
public void remove() {
//如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
登入後複製小結
在每個線程內部都有一個名為threadLocals 的成員變量, 該變量的類型為Hash Map , 其中key 為我們定義的ThreadLocal 變數的this 引用, value 則為我們使用set 方法設定的值。每個執行緒的本機變數存放在執行緒自己的記憶體變數threadLocals 中,如果目前執行緒一直不消亡, 那麼這些本機變數會一直存在, 所以可能會造成記憶體溢出, 因此使用完畢後要記得呼叫ThreadLocal 的remove 方法刪除對應線程的threadLocals 中的本地變數。
public void remove() { //如果当前线程的threadLocals 变量不为空, 则删除当前线程中指定ThreadLocal 实例的本地变量。 ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
ThreadLocal記憶體洩漏為什麼會出現記憶體洩漏?
ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個ThreadLocal沒有外部強引用來引用它,那麼系統GC 的時候,這個ThreadLocal勢必會被回收,- 這樣一來,ThreadLocalMap中就會出現key為null的Entry
- ,就沒有辦法存取這些key為null的Entry的value,
如果目前執行緒再遲遲不結束的話,這些key為null的Entry的value就會一直存在一條強引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠無法回收,造成記憶體洩漏。
其實,ThreadLocalMap的設計中已經考慮到這種情況,也加上了一些防護措施:在ThreadLocal的get(),set(),remove()的時候都會清除線程ThreadLocalMap裡所有key為null的value。但是這些被動的預防措施並不能保證不會記憶體洩漏: - 使用static的ThreadLocal,延長了ThreadLocal的生命週期,可能導致的記憶體洩漏
如果使用強引用:我們知道,ThreadLocalMap的生命週期基本上和Thread的生命週期一樣,當前線程如果沒有終止,那麼ThreadLocalMap始終不會被GC回收,而ThreadLocalMap持有對ThreadLocal的強引用,那麼ThreadLocal也不會被回收,當線程生命週期長,如果沒有手動刪除,則會造成kv累積,從而導致OOM
如果使用弱引用:弱引用中的物件具有很短的宣告週期,因為在系統GC時,只要發現弱引用,不管堆空間是否足夠,都會將物件進行回收。而當ThreadLocal的強引用被回收時,ThreadLocalMap所持有的弱引用也會被回收,如果沒有手動刪除kv,那麼會造成value累積,也會導致OOM
對比可知,使用弱引用至少可以保證不會因為map的key累積從而導致OOM,而對應的value可以透過remove,get,set方法在下一次呼叫時被清除。可見,記憶體洩漏的根源不是弱引用,而是ThreadLocalMap的生命週期和Thread一樣長,造成累積導致的
解決方法
既然問題的根源是value的累積造成OOM,那我們對症下藥,每次使用完ThreadLocal呼叫remove()
方法清理掉就行了。
以上是Java ThreadLocal類別如何使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

PHP適用於Web開發和內容管理系統,Python適合數據科學、機器學習和自動化腳本。 1.PHP在構建快速、可擴展的網站和應用程序方面表現出色,常用於WordPress等CMS。 2.Python在數據科學和機器學習領域表現卓越,擁有豐富的庫如NumPy和TensorFlow。
