一、ThreadLocal使用場景
在數據庫使用connection對象時,每個客戶都能使用自己的connection對象,防止出現客戶ClientA操作關閉ClientB的connection連接對象。
案例:https://zhuanlan.zhihu.com/p/82737256
二、ThreadLocal中的remove()使用
1,防止內存泄露
2,線程不安全
在ThreadLocal和線程池聯合使用的時候,會出現下個業務請求複用到上一個線程的情況,導致使用相同的ThreadLocal執行不同的業務邏輯。
public class ThreadLocalAndPool { private static ThreadLocal<Integer> variableLocal = ThreadLocal.withInitial(() -> 0); public static int get() { return variableLocal.get(); } public static void remove() { variableLocal.remove(); } public static void increment() { variableLocal.set(variableLocal.get() + 1); } public static void main(String[] args) { ExecutorService executorService = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(12)); for (int i = 0; i < 5; i++) { executorService.execute(() -> { long threadId = Thread.currentThread().getId(); int before = get(); increment(); int after = get(); System.out.println("threadid " + threadId + " before " + before + ", after " + after); }); } executorService.shutdown(); } }
threadid 12 before 0, after 1 threadid 13 before 0, after 1 threadid 12 before 1, after 2 threadid 13 before 1, after 2 threadid 12 before 2, after 3
三、爲什麼會出現內存泄露?
四、爲什麼使用弱引用?
從表面上看,發生內存泄漏,是因爲Key使用了弱引用類型。但其實是因爲整個Entry的key爲null後,沒有主動清除value導致。爲什麼使用弱引用而不是強引用?
官方文檔的說法:
To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.
爲了處理非常大和生命週期非常長的線程,哈希表使用弱引用作爲 key。
下面我們分兩種情況討論:
key 使用強引用:引用的ThreadLocal的對象被回收了,但是ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動刪除,ThreadLocal不會被回收,導致Entry內存泄漏。 key 使用弱引用:引用的ThreadLocal的對象被回收了,由於ThreadLocalMap持有ThreadLocal的弱引用,即使沒有手動刪除,ThreadLocal也會被回收。value在下一次ThreadLocalMap調用set,get,remove的時候會被清除。
比較兩種情況,我們可以發現:由於ThreadLocalMap的生命週期跟Thread一樣長,如果都沒有手動刪除對應key,都會導致內存泄漏,但是使用弱引用可以多一層保障:弱引用ThreadLocal不會內存泄漏,對應的value在下一次ThreadLocalMap調用set,get,remove的時候會被清除。
因此,ThreadLocal內存泄漏的根源是:由於ThreadLocalMap的生命週期跟Thread一樣長,如果沒有手動刪除對應key的value就會導致內存泄漏,而不是因爲弱引用。