ThreadLocal需要解決的問題是什么
- ThreadLocal被用來“解決多線程并發(fā)”問題
- ThreadLocal與同步機(jī)制的區(qū)別
- 同步機(jī)制:對于多線程訪問的共享資源,通過“鎖”對變量進(jìn)行限制,此時變量只有一份;
- ThreadLocal:在多線程環(huán)境下,每個線程拷貝一份變量副本,各個線程使用自己的那一份(即所謂的Local),彼此之間互不影響;
ThreadLocal如何解決多線程并發(fā)問題
以下代碼基于JDK1.8版本
- 既然每個線程都會拷貝一份變量,那么我們先看一下這個變量會存在哪里。首先來看Thread類:
public class Thread implements Runnable {
//......
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//......
}
顯然,變量的存儲肯定與ThreadLocalMap有關(guān)
2.ThreadLocal類的ThreadLocalMap:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
首先,ThreadLocalMap是一個靜態(tài)內(nèi)部類;其次,通過WeakReference<ThreadLocal<?>>的修飾,ThreadLocal變?yōu)橐粋€弱引用指向類;再次,還有一個靜態(tài)內(nèi)部類Entry存儲鍵值,其中Key為弱引用的ThreadLocal本身(通過super(k)實(shí)例化)。
再看一下ThreadLocalMap的內(nèi)部函數(shù):
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
先初始化一個長度為16的Entry數(shù)組,然后通過哈希算法計(jì)算出鍵值的映射關(guān)系,再存到數(shù)組中就OK了。**順帶一提,這里是一個無鎖并發(fā)的哈希實(shí)現(xiàn),具體可以參考[世界上最簡單的無鎖哈希表](http://blog.jobbole.com/39186/)**
對于其他的方法,暫時省略......
ThreadLocal內(nèi)存泄漏問題
為什么ThreadLocal會產(chǎn)生內(nèi)存泄漏
ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個ThreadLocal沒有外部強(qiáng)引用來引用它,那么系統(tǒng) GC 的時候,這個ThreadLocal勢必會被回收,這樣一來,ThreadLocalMap中就會出現(xiàn)key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當(dāng)前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會一直存在一條強(qiáng)引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠(yuǎn)無法回收,造成內(nèi)存泄漏。
其實(shí),ThreadLocalMap的設(shè)計(jì)中已經(jīng)考慮到這種情況,也加上了一些防護(hù)措施:在ThreadLocal的get(),set(),remove()的時候都會清除線程ThreadLocalMap里所有key為null的value。
但是這些被動的預(yù)防措施并不能保證不會內(nèi)存泄漏:
使用線程池的時候,這個線程執(zhí)行任務(wù)結(jié)束,ThreadLocal對象被回收了,線程放回線程池中不銷毀,這個線程一直不被使用,導(dǎo)致內(nèi)存泄漏。
分配使用了ThreadLocal又不再調(diào)用get(),set(),remove()方法,那么這個期間就會發(fā)生內(nèi)存泄漏。
如何避免ThreadLocal內(nèi)存泄漏
每次使用完ThreadLocal,都要記得手動調(diào)用remove()方法,清除數(shù)據(jù)。