Java里,每個線程都有自己的ThreadLocalMap,里邊存着自己私有的對象。Map的Entry里,key為ThreadLocal對象,value即為私有對象T。在spring MVC中,常用ThreadLocal保存當前登陸用戶信息,這樣線程在任意地方都可以取到用戶信息了。
public class UserContext { private static final ThreadLocal<UserInfo> userInfoLocal = new ThreadLocal<UserInfo>(); public static UserInfo getUserInfo() { return userInfoLocal.get(); } public static void setUserInfo(UserInfo userInfo) { userInfoLocal.set(userInfo); } public static void clear() { userInfoLocal.remove(); } }
這里,跳過ThreadLocal和ThreadLocalMap的工作原理及場景不講,主要來說說Entry為什么是WeakReference?
/** * 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; } }
先來看看WeakReference的作用,百度一下:
WeakReference是Java語言規范中為了區別直接的對象引用(程序中通過構造函數聲明出來的對象引用)而定義的另外一種引用關系。WeakReference標志性的特點是:reference實例不會影響到被應用對象的GC回收行為(即只要對象被除WeakReference對象之外所有的對象解除引用后,該對象便可以被GC回收),只不過在被對象回收之后,reference實例想獲得被應用的對象時程序會返回null。
我的理解就是,WeakReference對應用的對象userInfoLocal是弱引用,不會影響到userInfoLocal的GC行為。如果是強引用的話,在線程運行過程中,我們不再使用userInfoLocal了,將userInfoLocal置為null,但userInfoLocal在線程的ThreadLocalMap里還有引用,導致其無法被GC回收(當然,可以等到線程運行結束后,整個Map都會被回收,但很多線程要運行很久,如果等到線程結束,便會一直占着內存空間)。而Entry聲明為WeakReference,userInfoLocal置為null后,線程的threadLocalMap就不算強引用了,userInfoLocal就可以被GC回收了。map的后續操作中,也會逐漸把對應的"stale entry"清理出去,避免內存泄漏。
所以,我們在使用完ThreadLocal變量時,盡量用threadLocal.remove()來清除,避免threadLocal=null的操作。前者remove()會同時清除掉線程threadLocalMap里的entry,算是徹底清除;而后者雖然釋放掉了threadLocal,但線種threadLocalMap里還有其"stale entry",后續還需要處理。