ThreadLocal,我们依然从一个例子看起。
public class ThreadLocalTest2 {
public static void main(String[] args) {
// ①,强引用tl1
ThreadLocal tl1 = new ThreadLocal();
tl1.set(11);
System.gc();
tl1.get();
System.gc();
// tl1.remove();
// ②,没有强引用
new ThreadLocal<>().set(22);
System.gc();
// ③,方法里的tl
ThreadLocalTest2 test2 = new ThreadLocalTest2();
test2.test1();
System.gc();
Thread thread = Thread.currentThread();
System.out.println(thread);
}
void test1() {
ThreadLocal<Integer> tl = new ThreadLocal<>();
tl.set(33);
// tl.remove();
}
}
debug模式下,我们看下thread里ThreadLocalMap里的情况,如下图。

①因为存在强引用,所以对应的referent不为null。
②没有强引用,垃圾回收后,referent为null。
③ThreadLocal仅在test1()方法中有效,所以垃圾回收后,referent为null。
public class ThreadLocal<T> {
...
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
}
...
}
从线程到ThreadLocal.set(value)的value的引用关系就是这样的
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
设置线程本地变量的内容。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
获取线程本地变量的value值
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
protected T initialValue() {
return null;
}
移除线程本地变量。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocal使用完后,一定要调用remove()!!!
不手动调用remove()方法,
1.容易发生内存泄漏。
2.线程池场景下后面的线程就有可能获取到上个线程遗留下来的value值,造成bug。
这里也用一个例子,来说明value为什么不是弱引用
public class TLLeakTest {
public static void main(String[] args) {
// ①,key和value均为WeakReference
Map<WeakReference<Integer>, WeakReference<Integer>> map = new HashMap<>(8);
WeakReference<Integer> key = new WeakReference<>(1);
WeakReference<Integer> value = new WeakReference<>(777);
map.put(key,value);
System.gc();
System.out.println("get1: " + map.get(key).get());
// ②,key为WeakReference,value为HardReference
Map<WeakReference<Integer>, Integer> map2 = new HashMap<>(8);
WeakReference<Integer> key2 = new WeakReference<>(1);
map2.put(key2, 999);
System.gc();
System.out.println("get2: "+ map2.get(key2));
}
}
get1: null
get2: 999
以上结果,可以说明为什么value不能为弱引用。
如果value也为弱引用,那么得到的结果会为null。
使用完ThreadLocal,如果没有及时remove(),那么key因为是弱引用就会被回收,但value是强引用,value没被回收,导致value永远存在,value越来越多后,造成内存泄漏。