問題引出:
ThreadLocal是為了解決什么問題而產生的?
ThreadLocal發生內存泄漏的根本原因是什么?
如何避免內存泄漏的發生?
定義
??為了解決多個線程同時操作程序中的同一個變量而導致的數據不一致性的問題。
??假設現在有兩個線程A和B,想要同時使用程序中的某個變量,如果想要保持這個變量的數據一致性,該怎么做?
(1)首先最直接的方法就是對變量進行加鎖,一次只允許一個線程進行操作;但是這種加鎖的方式效率不高,當有很多線程想要操作變量時,沒有持有鎖的線程只能等待。
(2)而Threadlocal解決的方式就比較優雅了,它是怎么做的呢?一句話概括:為每一個線程創建了變量的副本。
大白話解釋:給兩個線程分別分配了一個容器,線程在各自的容器中對各自的變量副本進行操作,互不影響,實現了線程之間的隔離。
類圖
??在ThreadLocal的類中,存在一個核心的內部類ThreadLocalMap;在ThreadLocalMap的類中,也存在著一個核心的內部類Entry。
圖解
??對于每一個線程,都維護著自己的ThreadLocalMap,而這個ThreadLocalMap中存放的變量就是Entry類型的,Entry類型的變量的key就是一個threadLocal對象,value就是你想要存的值。如下圖所示:
思考:
一個ThreadLocal對象是否可以對應多個值(Object v)?
內存泄漏
在宏觀上,內存泄漏是指,由于錯誤或者疏忽,未能正確釋放已經不再使用的內存。
那么ThreadLocal發生內存泄漏的根本原因是什么呢?
根本原因在于ThreadLocalMap中存的是一個個的Entry對象,問題就出在這個Entry對象中。
static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
仔細看,Entry對象中的ThreadLocal變量是一個弱引用,這個倒沒問題,jvm會在合適的時機將其內存自動進行回收;但是Entry對象中的Object值是一個強引用,問題就出現在這里,jvm寧愿報內存溢出的錯,也不愿意主動去回收一個強引用的內存。
這就會出現一種什么情況呢?我的ThreadLocal變量的內存已經被jvm回收掉了,在ThreadLocalMap中已經不再需要以當前被回收掉的ThreadLocal變量為key的Entry了,但是這個Entry中的value一直得不到回收,因為它是一個強引用。由此發生了內存泄漏。
思考:ThreadLocal一定會發生內存泄漏嗎?
答:不一定。解答此問題的關鍵在于要明白ThreadLocal的生命周期是和當前線程的生命周期是一樣的。
如果當前線程被銷毀了,那么線程中的所有變量都將被銷毀,包括ThreadLocalMap對象、Entry對象等等;
如果當前線程沒有被銷毀,它只是被回收到線程池中了(比如當前線程是一個核心線程),則就會發生內存泄漏。
內存泄漏的避免
??在使用完ThreadLocal之后,調用它的remove方法,主動去清除不再使用的內存。
參考鏈接:
Threadlocal為什么會有內存泄漏泄漏,如何解決
java中的引用