ThreadLocal 是什么问拘?
是一個關(guān)于創(chuàng)建線程局部變量的類。
每個線程都有一個保存值的 ThreadLocalMap 對象,ThreadLocal 的值就存放在了當(dāng)前線程的 ThreadLocalMap 成員變量中赢笨,所以只能在本線程訪問蛮寂,其他線程不能訪問蔽午。
public class Thread implements Runnable {
...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
從當(dāng)前線程的ThreadLocalMap中取出當(dāng)前線程對應(yīng)的變量的副本【注意,變量是保存在線程中的酬蹋,而不是保存在ThreadLocal變量中】
ThreadLocalMap維護了Entry環(huán)形數(shù)組及老,數(shù)組中元素Entry的邏輯上的key為某個ThreadLocal對象(實際上是指向該ThreadLocal對象的弱引用),value為代碼中該線程往該ThreadLoacl變量實際塞入的值范抓。
從ThreadLocal讀一個值可能遇到的情況:根據(jù)入?yún)hreadLocal的threadLocalHashCode對表容量取模得到 index
- 如果index對應(yīng)的slot就是要讀的threadLocal骄恶,則直接返回結(jié)果
- 調(diào)用getEntryAfterMiss線性探測,過程中每碰到無效slot匕垫,調(diào)用- expungeStaleEntry進行段清理僧鲁;如果找到了key,則返回結(jié)果entry
- 沒有找到key象泵,返回null
ThreadLocal的set方法可能會有的情況寞秃。
- 探測過程中slot都不無效,并且順利找到key所在的slot偶惠,直接替換即可
- 探測過程中發(fā)現(xiàn)有無效slot春寿,調(diào)用replaceStaleEntry,效果是最終一定會把key和value放在這個slot忽孽,并且會盡可能清理無效slot
-- 在replaceStaleEntry過程中堂淡,如果找到了key,則做一個swap把它放到那個無效slot中扒腕,value置為新值
-- 在replaceStaleEntry過程中绢淀,沒有找到key,直接在無效slot原地放entry - 探測沒有發(fā)現(xiàn)key瘾腰,則在連續(xù)段末尾的后一個空位置放上entry皆的,這也是線性探測法的一部分。放完后蹋盆,做一次啟發(fā)式清理费薄,如果沒清理出去key硝全,并且當(dāng)前table大小已經(jīng)超過閾值了,則做一次rehash楞抡,rehash函數(shù)會調(diào)用一次全量清理slot方法也即expungeStaleEntries伟众,如果完了之后table大小超過了threshold – threshold / 4,則進行擴容2倍
ThreadLocal內(nèi)存泄漏問題召廷?
每個thread中都存在一個map, map的類型是ThreadLocal.ThreadLocalMap. Map中的key為一個threadlocal實例. 這個Map的確使用了弱引用,不過弱引用只是針對key. 每個key都弱引用指向threadlocal. 當(dāng)把threadlocal實例置為null以后,沒有任何強引用指向threadlocal實例,所以threadlocal將會被gc回收. 但是,我們的value卻不能回收,因為存在一條從current thread連接過來的強引用. 只有當(dāng)前thread結(jié)束以后, current thread就不會存在棧中,強引用斷開, Current Thread, Map, value將全部被GC回收凳厢。所以得出一個結(jié)論就是只要這個線程對象被gc回收,就不會出現(xiàn)內(nèi)存泄露竞慢,但在threadLocal設(shè)為null和線程結(jié)束這段時間不會被回收的先紫,就發(fā)生了我們認為的內(nèi)存泄露。其實這是一個對概念理解的不一致筹煮,也沒什么好爭論的遮精。最要命的是線程對象不被回收的情況,這就發(fā)生了真正意義上的內(nèi)存泄露败潦。比如使用線程池的時候本冲,線程結(jié)束是不會銷毀的,會再次使用的就可能出現(xiàn)內(nèi)存泄露 劫扒。(在web應(yīng)用中檬洞,每次http請求都是一個線程,tomcat容器配置使用線程池時會出現(xiàn)內(nèi)存泄漏問題)