一、ThreadLocal的概述
1、Thread對象有一個屬性ThreadLocal.ThreadLocalMap,該數據結構底層為數組,用於存放當前線程的ThreadLocal實例。類似hashMap的實現,但hash槽位上不是鏈表。
2、ThreadLocal.ThreadLocalMap的hash槽位上是一個弱引用的指針。具體作用:

public void func1() { ThreadLocal tl = new ThreadLocal<Integer>(); //line1 tl.set(100); //line2 tl.get(); //line3 }
當func1方法執行完畢后,棧幀銷毀,強引用 tl 也就沒有了,但此時線程的ThreadLocalMap里某個entry的 k 引用還指向這個對象。若這個k 引用是強引用,就會導致k指向的ThreadLocal對象及v指向的對象不能被gc回收,造成內存泄漏,但是弱引用就不會有這個問題(弱引用及強引用等這里不說了)。使用弱引用,就可以使ThreadLocal對象在方法執行完畢后順利被回收,而且entry的k引用指向為null,此后我們調用get,set或remove方法時,就會嘗試刪除key為null的entry,可以釋放value對象所占用的內存。
概括說就是:在方法中新建一個ThreadLocal對象,就有一個強引用指向它,在調用set()后,線程的ThreadLocalMap對象里的Entry對象又有一個引用 k 指向它。如果后面這個引用 k 是強引用就會使方法執行完,棧幀中的強引用銷毀了,對象還不能回收,造成嚴重的內存泄露。
注意:雖然弱引用,保證了k指向的ThreadLocal對象能被及時回收,但是v指向的value對象是需要ThreadLocalMap調用get、set時發現k為null時才會去回收整個entry、value,因此弱引用不能保證內存完全不泄露。我們要在不使用某個ThreadLocal對象后,手動調用remoev方法來刪除它,尤其是在線程池中,不僅僅是內存泄露的問題,因為線程池中的線程是重復使用的,意味着這個線程的ThreadLocalMap對象也是重復使用的,如果我們不手動調用remove方法,那么后面的線程就有可能獲取到上個線程遺留下來的value值,造成bug。
3、ThreadLocal自身有一個計數器,用於生產每一個threadLocal對象的hash值,該值會和ThreadLocalMap中的數據長度進行hash運算,確定threadLocal的在數組中的存儲位置。