Liunx安裝Nacos 一,準備安裝包 github下載點 同時請確認Linux已安裝jdk 二,在/usr/local/目錄下創建一個文件夾用於上傳和解壓Nacos cd /usr/local/ #這裡創建文件夾名字可隨意,解壓後會生成一個名為nacos的文件夾,後續會移動至/usr/local ...
一、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 時回收