一、ThreadLocal 簡介
ThreadLocal實體通常作為靜態的私有的(private static)欄位出現在一個類中,這個類用來關聯一個執行緒,ThreadLocal是一個執行緒級別的區域變數,下面是執行緒區域變數(ThreadLocal variables)的關鍵點:
A、當使用ThreadLocal維護變數時,若多個執行緒訪問ThreadLocal實體,ThreadLocal為每個使用該變數的執行緒提供了一個獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其他執行緒所對應的副本,
B、從執行緒的角度看,目標變數就像是執行緒的本地變數,這也是類名中Local所要表達的意思,
二、ThreadLocal 特點及用途:
1.ThreadLocal是單執行緒內共享資源,多執行緒間無法共享(即執行緒A訪問不了執行緒B中ThreadLocal存放的值);
2.ThreadLocal是本地變數,無法跨jvm傳遞;
3.ThreadLocal的出現可以減少通過引數來傳遞(使代碼更加簡潔,降低耦合性),Hibernate中的OpenSessionInView,就始終保證當前執行緒只有一個在使用中的Connection(或Hibernate Session),代碼如下:
1 public class ConnectionManager {
2
3 /** 執行緒內共享Connection,ThreadLocal通常是全域的,支持泛型 */
4 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
5
6 public static Connection getCurrConnection() {
7 // 獲取當前執行緒內共享的Connection
8 Connection conn = threadLocal.get();
9 try {
10 // 判斷連接是否可用
11 if(conn == null || conn.isClosed()) {
12 // 創建新的Connection賦值給conn(略)
13 // 保存Connection
14 threadLocal.set(conn);
15 }
16 } catch (SQLException e) {
17 // 例外處理
18 }
19 return conn;
20 }
21
22 /**
23 * 關閉當前資料庫連接
24 */
25 public static void close() {
26 // 獲取當前執行緒內共享的Connection
27 Connection conn = threadLocal.get();
28 try {
29 // 判斷是否已經關閉
30 if(conn != null && !conn.isClosed()) {
31 // 關閉資源
32 conn.close();
33 // 移除Connection
34 threadLocal.remove();
35 conn = null;
36 }
37 } catch (SQLException e) {
38 // 例外處理
39 }
40 }
41 }三、ThreadLocal 實作原理
定義了一個Map(非普通map) 結構,

例如:
定義一個 ThreadLocal 物件
public static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
key 就是 integerThreadLocal
value 就是 Integer 型別的值
在 Thread 里面持有該物件的參考,也就是說,不同的執行緒,有各自的 ThreadLocalMap
![]()
例如:A 執行緒,有一個 ThreadLocalMap,key 是一系列 ThreadLocal 物件,value 是 A 執行緒使用這一系列 ThreadLocal 對應的值;同理,B 執行緒,也有一個 ThreadLocalMap,,key 是一系列 ThreadLocal 物件,value 是 B 執行緒使用這一系列 ThreadLocal 對應的值,
所以,現在有兩個 ThreadLocal 物件,被3個執行緒 TA TB TC 使用:
public static ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>(); public static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>(); TA:integerThreadLocal.set(1);stringThreadLocal.set("A"); TB:integerThreadLocal.set(2);stringThreadLocal.set("B"); TC:integerThreadLocal.set(3);stringThreadLocal.set("C");
最終存盤結構是:
{{"thread":TA,"threadLocalMap":{"integerThreadLocal":1,"stringThreadLocal":"A"}},
{"thread":TB,"threadLocalMap":{"integerThreadLocal":2,"stringThreadLocal":"B"}},
{"thread":TC,"threadLocalMap":{"integerThreadLocal":3,"stringThreadLocal":"C"}}}
格式化后,如下:
{
"TA": {
"integerThreadLocal": 1,
"stringThreadLocal": "A"
},
"TB": {
"integerThreadLocal": 2,
"stringThreadLocal": "B"
},
"TC": {
"integerThreadLocal": 3,
"stringThreadLocal": "C"
}
}
使用時:stringThreadLocal.get();

四、ThreadLocal 的弱參考
強參考:
Object obj = new Object();
只要 obj 不為空,gc 的時候 就不會回收 obj,此為強參考,
弱參考:
WeakReference<Object> weakRef = new WeakReference<>(new Object());
發生 gc 時,盡管 括號里面的物件 不為空,也會被回收,但是 weakRef 是強參考,不會被回收,
ThreadLocalMap 的 key 是 ThreadLocal 物件的弱參考,即 gc 以后,ThreadLocalMap 的 key 將指向 null,而 value 依舊是強參考,不會被回收,會造成記憶體泄露,
所以,ThreadLocal 的弱參考,解決了記憶體泄露嗎?
解決了一部分,key 確實被回收了,但是 value 不是弱參考,沒有被回收,還是有可能造成記憶體泄露,
五、ThreadLocal 解決記憶體泄露
兩種記憶體泄露情況:
1.發生了 gc,弱參考被回收了,ThreadLocalMap 的 key 指向了 null,而 value 是強參考,不會被回收,會造成記憶體泄露
2.執行緒被執行緒池管理,不會銷毀,其對應的 ThreadLocalMap 也不會被回收,會造成記憶體泄露
解決方案:
1.主動呼叫 ThreadLocal 的 remove 方法,
![]()
2.ThreadLocal 在 set、get、remove 時,會自動移除 key 為 null 的 Entry,由于,ThreadLocalMap 的 key 是弱參考,gc 后 key 會指向 null,所以,這部分會被置為 null,方便下次 gc 時回收

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/484576.html
標籤:Java
上一篇:Kotlin無緣無故地嘗試將Double轉換為Int?
下一篇:如何確定陣列中最常出現的數字?
