文章目錄
- ThreadLocal
- 源碼分析:
- set方法
- get方法
- remove方法
- ThreadLocal內存泄漏問題
ThreadLocal
ThreadLocal提供了線程局部變量,每個線程都可以通過set和get方法來對這個變量進行操作,但不會和其他線程的局部變量沖突,實現了線程的數據隔離
源碼分析:
set方法
public void set(T value) {Thread t = Thread.currentThread();//getMap就是為了獲取當前線程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null)//如果map存在就直接以這個ThreadLocal為鍵,設置鍵和值map.set(this, value);else//否則就為他創建一個ThreadLocalMap,并設置第一個鍵和值createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
get方法
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {//如果當前線程的ThreadLocalMap存在,就嘗試獲取對應鍵值對ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//如果threadLocals不存在或者當前的ThreadLocal不存在于這個map中return setInitialValue();
}
private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)//當前ThreadLocal不存在于map中,就加上,設置value為空map.set(this, value);else//如果map不存在就創建,并設置鍵值createMap(t, value);return value;
}
protected T initialValue() {return null;
}
remove方法
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}
private void remove(ThreadLocal<?> key) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {if (e.get() == key) {//弱引用中的清除方法e.clear();//將key為null的鍵值對清除掉,get和set方法底層也有用到expungeStaleEntry(i);return;}}
}
private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);
}
總結:
可以看出,ThreadLocal中并不存儲值,只是作為一個key來讓線程從ThreadLocalMap中獲取value,從而實現了線程之間的數據隔離
- 每個線程都維護著一個ThreadLocalMap,ThreadLocalMap是ThreadLocal的內部類
- ThreadLocal中的set,實際上是向當前線程的ThreadLocalMap中設置值,鍵為創建的ThreadLocal對象
ThreadLocal內存泄漏問題
內存泄漏:程序中間動態分配了內存,但在程序結束時沒有釋放這部分內存,從而造成那部分內存不可用的情況
內存溢出:要求分配的內存超過了系統能給的
ThreadLocal中作為map中的key使用,而且ThreadLocalMap中的key是弱引用,弱引用對象在gc時會被回收,而ThreadLocalMap和Thread的生命周期一樣長,就會存在key為null的情況,value訪問不到,從而引發內存泄漏。所以,使用ThreadLocal時最后最好調用remove方法顯式調用expungeStaleEntry方法手動刪除key為null的value,防止value的積累
ThreadLocal的get和set方法某些時候也會調用expungeStaleEntry方法,但這是不及時的,而且不一定每次都會執行