1. 程式人生 > 其它 >乾貨|APP自動化Android特殊控制元件Toast識別

乾貨|APP自動化Android特殊控制元件Toast識別

一、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 時回收