ThreadLocal是如何保证只被当前线程访问的呢?我们先来看一下set()方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在set时,首先获得当前线程对象,然后通过getMap(t)拿到线程的ThreadLocalMap,并将值设入ThreadLocalMap中。
而ThreadLocalMap可以理解为一个Map(虽然不是,但是你可以把它简单理解成HashMap),下面看一下getMap(t)的代码
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
实际上ThreadLocalMap是Thread线程类的局部变量。但是类是ThreadLocal.ThreadLocalMap。
ThreadLocal.ThreadLocalMap threadLocals = null;
下面我们看一下ThreadLocal.get()的代码
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();
}
在了解ThreadLocal的内部实现之后,我们自然会引出一个问题。那就是这些变量是维护在Thread类内部的,这也意味着,只要线程不退出,对象的引用将一直存在。
当线程退出时,Thread类会清除ThreadLocalMap
/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
*/
private void exit() {
if (group != null) {
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
但是,如果我们使用的是线程池,线程可能不会真的退出,如果将一些大对象保存在ThreadLocal中,那这些对象占用的内存可能永远会得不到释放。这就是ThreadLocal的内存泄露问题 。
如果希望及时回收不再使用的对象,可以通过ThreadLocal.remove()方法移除。
0条评论