效果
gc後key爲null,但是值不爲null。
需要注意的是,這裏立即釋放了對threadLocal實例的強引用,幫助gc回收。查看弱引用的使用方法
原因
ThreadLocal#set後會將threadLocal實例本身作爲key 放入 Thread.currentThread().threadLocalMap
中,與set的value構成一對Entry。而Entry使用了threadLocal的實例作爲 弱引用
。因此當發生gc的時候,弱引用的key會被回收掉,而作爲強引用的value還存在。
作爲key的弱引用的ThreadLocal
此次借用網圖幫助理解
注意
正如我在註釋中寫的那樣,如果沒有失去對ThreadLocal本身的強引用,那麼不會回收threadLocal。
而我們平時代碼中寫的那樣,使用static
修飾threadLocal保留一個全局的threadLocal方便傳遞其他value(threadLocal一直被強引用)。這樣就不會讓gc回收 作爲key的threadLocal。即不會導致key爲null。
使用ThreadLocal關鍵之處還是在於,使用完畢要記得remove。特別是在線程池中使用的時候。(否則會等到下一次set的時候才替換掉value–>作爲key的threadLocal爲同一個所以是替換)
threadLocal被強引用 引用,無法被回收
代碼
/**
* 測試ThreadLocal 在gc後引發的threadLocalMap的key爲null,但value不爲null的情況
* @Author thewindkee
* @Date 2019/12/27 9:28
*/
public class Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
Thread t = new Thread(()->test("abc",false));
t.start();
t.join();
System.out.println("--gc後--");
Thread t2 = new Thread(() -> test("def", true));
t2.start();
t2.join();
}
private static void test(String s,boolean isGC) {
try {
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
threadLocal.set(s);
threadLocal = null;//失去對threadLocal的強引用 ,help gc
if (isGC) {
System.gc();
}
Thread t = Thread.currentThread();
Class<? extends Thread> clz = t.getClass();
Field field = clz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object threadLocalMap = field.get(t);
Class<?> tlmClass = threadLocalMap.getClass();
Field tableField = tlmClass.getDeclaredField("table");
tableField.setAccessible(true);
Object[] arr = (Object[]) tableField.get(threadLocalMap);
for (Object o : arr) {
if (o != null) {
Class<?> entryClass = o.getClass();
Field valueField = entryClass.getDeclaredField("value");
Field referenceField = entryClass.getSuperclass().getSuperclass().getDeclaredField("referent");
valueField.setAccessible(true);
referenceField.setAccessible(true);
System.out.println(String.format("弱引用key:%s,值:%s", referenceField.get(o), valueField.get(o)));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}