日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

快速搞懂ThreadLocal实现原理

發布時間:2024/1/23 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 快速搞懂ThreadLocal实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄
一、ThreadLocal使用案例
二、ThreadLocal類的實現原理
2.1 核心方法set()
2.2 核心方法get()
2.3 核心方法remove()
三、ThreadLocal.ThreadLocalMap結構分析
四、ThreadLocal內存泄漏問題
參考資料
維持線程封閉性可以通過Ad-hoc線程封閉、棧封閉來實現,一種更加規范的方法是使用ThreadLocal類。ThreadLocal類提供線程局部變量,通過get、set等方法訪問變量,為每個使用該變量的線程創建一個獨立的副本。

一、ThreadLocal使用案例
案例中只開啟了一個線程threadA,展示了在線程內部設置、獲取、清除局部變量。

public class ThreadLocalTest {
? ? // 初始化ThreadLocal變量
? ? static ThreadLocal<String> localVariable = new ThreadLocal<>();

? ? static void print(String str) {
? ? ? ? // 打印當前線程本地內存中的變量值
? ? ? ? System.out.println(str + ": " + localVariable.get());
? ? ? ? // 清除當前線程本地內存中的變量
? ? ? ? localVariable.remove();
? ? }

? ? public static void main(String[] args) {
? ? ? ? // 創建線程A
? ? ? ? Thread threadA = new Thread(new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? // 設置線程A中的本地變量的值
? ? ? ? ? ? ? ? localVariable.set("threadA localVariable");
? ? ? ? ? ? ? ? print("threadA");
? ? ? ? ? ? ? ? // 獲取線程A中的本地變量的值
? ? ? ? ? ? ? ? System.out.println("threadA remove after: " + localVariable.get());
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? // 啟動線程
? ? ? ? threadA.start();
? ? }
}

運行結果:
threadA: threadA localVariable
threadA remove after: null

案例二:線程唯一標識符生成器,為每個調用ThreadId.get()方法的線程創建id。

public class ThreadId {
? ? // 下一個要被分配的線程id
? ? private static final AtomicInteger nextId = new AtomicInteger(0);

? ? // 線程局部變量
? ? private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
? ? ? ? @Override
? ? ? ? protected Integer initialValue() {
? ? ? ? ? ? return nextId.getAndIncrement();
? ? ? ? }
? ? };

? ? // 返回當前線程唯一的id
? ? public static int get() {
? ? ? ? return threadId.get();
? ? }
}

二、ThreadLocal類的實現原理
在Thread類中有一個threadLocals成員變量,其類型是ThreadLocalMap,默認情況下為null。

ThreadLocal.ThreadLocalMap threadLocals = null;
1
當某線程首次調用ThreadLocal變量的get或set方法時,會進行對象創建。在線程退出時,當前線程的threadLocals變量被清空。

private void exit() {
? ? ...
? ? threadLocals = null;
? ? inheritableThreadLocals = null;
? ? ...
}

每個線程的局部變量不是存放于ThreadLocal實例中,而是存放于線程的threadLocals變量,即線程內存空間中。threadLocals變量本質上是Map數據結構,可以存放多個ThreadLocal變量鍵值對。

【助解】ThreadLocal類可以看出一個外殼,線程中調用某ThreadLocal變量的set方法可以將變量值放入到該線程的threadLocals變量中,數據格式是<當前線程中該ThreadLocal變量的this引用,變量值>。當調用線程調用ThreadLocal變量的get方法時,從當前線程的threadLocals變量中取出key(引用)對應的value值。

2.1 核心方法set()
將ThreadLocal變量的當前線程副本的值設置為指定value值。

public void set(T value) {
? ? // 獲取調用方法的當前線程
? ? Thread t = Thread.currentThread();
? ? // 獲取當前線程自身的threadLocals變量
? ? ThreadLocalMap map = getMap(t);
? ? if (map != null)?
? ? ? ? // map不為空,則設置
? ? ? ? map.set(this, value);
? ? else?
? ? ? ? // map為空,說明第一次調用,初始化線程的threadLocals變量
? ? ? ? createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
? ? return t.threadLocals;
}

void createMap(Thread t, T firstValue) {
? ? t.threadLocals = new ThreadLocalMap(this, firstValue);
}

線程的threadLocals變量,即ThreadLocal.ThreadLocalMap,是HashMap結構,它的key是當前ThreadLocal的實例對象引用,value值是該ThreadLocal實例對象調用set方法設置的值。

2.2 核心方法get()
返回ThreadLocal變量在當前線程副本中的值。如果當前線程中沒有該變量的值,返回值會被首次初始化為initialValue()方法的值。

public T get() {
? ? // 獲取當前線程以及其threadLocals變量
? ? Thread t = Thread.currentThread();
? ? ThreadLocalMap map = getMap(t);
? ? // 如果threadLocals變量不為空
? ? if (map != null) {
? ? ? ? // 根據當前ThreadLocal對象應用獲取Entry,存在則直接返回value值
? ? ? ? ThreadLocalMap.Entry e = map.getEntry(this);
? ? ? ? if (e != null) {
? ? ? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? ? ? T result = (T)e.value;
? ? ? ? ? ? return result;
? ? ? ? }
? ? }
? ? // threadLocals為空,初始化當前線程threadLocals變量
? ? return setInitialValue();
}

// threadLocals存在,設置初始值;不存在,初始化threadLocals變量
private T setInitialValue() {
? ? // 返回當前ThreadLocal變量的當前線程初始值
? ? T value = initialValue();
? ? Thread t = Thread.currentThread();
? ? ThreadLocalMap map = getMap(t);
? ? if (map != null)
? ? ? ? map.set(this, value);
? ? else
? ? ? ? createMap(t, value);
? ? return value;
}

protected T initialValue() {
? ? return null;
}

2.3 核心方法remove()
當前線程threadLocals變量存在的話,刪除當前線程的ThreadLocal實例對象。

public void remove() {
? ? ThreadLocal.ThreadLocalMap var1 = this.getMap(Thread.currentThread());
? ? if (var1 != null) {
? ? ? ? var1.remove(this);
? ? }
}

三、ThreadLocal.ThreadLocalMap結構分析
ThreadLocalMap內部類實現細節,由于內容較多獨立成了一篇博客。ThreadLocal.ThreadLocalMap實現細節

四、ThreadLocal內存泄漏問題
每個線程的ThreadLocal變量都存放在該線程的threadLocals變量中,如果當前線程一直不退出,這些ThreadLocal變量會一直存在,因此可能會導致內存泄漏。通過調用ThreadLocal類的remove方法避免這一問題。

ThreadLocalMap中采用ThreadLocal弱引用作為Entry的key,如果一個ThreadLocal沒有外部強引用來引用它,下一次系統GC時,這個ThreadLocal必然會被回收,ThreadLocalMap中就會出現key為null的Entry。

ThreadLocal類的set、get、remove方法都可能觸發對key為null的Entry清理操作。expungeStaleEntry方法會清空Entry及其value,Entry會在下次GC被回收。

如果當前線程一直在運行,并且一直不執行get、set、remove方法,這些key為null的Entry的value就會一直存在一條強引用鏈:Thread Ref -> Thread -> ThreadLocalMap -> Entry -> value,導致這些key為null的Entry的value永遠無法回收,造成內存泄漏。

參考資料
《Java并發編程實戰》
《Java并發編程之美》
?

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的快速搞懂ThreadLocal实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。