ThreadLocal底层实现内部类:ThreadLocalMap
一、ThreadLocal的set方法源码分析:
(1)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
1、Thread t = Thread.currentThread();
获取当前线程对象t
2、ThreadLocalMap map = getMap(t);
获取ThreadLocalMap对象,调用getMap方法,参数:Thread t,将当前对象传入到getMap方法,getMap的源码如(2)所示:
(2)
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
通过代码分析得知:map的值是当前对象t中定义的ThreadLocalMap对象【也就是说map是属于当前线程t中的属性】。
3、如果map!=null,则执行map.set(this,value)。ThreadLocalMap中的key为ThreadLocal<?>对象。this将当前的ThreadLocal对象作为key。
4、如果map为null,则创建map,调用createMap(t,value),createMap源码如(3)所示:
(3)
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
通过源码得知,初始化当前线程对象t的threadLocals实例。并且将value保存到ThreadLocalMap中,将当前的ThreadLocal对象作为key。
注意:ThreadLocalMap实例是当前线程对象的属性,并且ThreadLocalMap中的key是当前ThreadLocal对象。
二、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();
}
1、Thread t = Thread.currentThread();获取当前线程对象t。
2、ThreadLocalMap map = getMap(t);通过以上源码得知,返回当前线程对象t的threadLocals实例。
3、如果map!=null,执行:ThreadLocalMap.Entry e = map.getEntry(this);源码参考(2-1):
(2-1)
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
3.1、e.get() 调用的是WeakReference中的get方法,返回的是ThreadLocal对象,即ThreadLocalMap的key值。
如果e.get()==key,则返回entry对象。否则,执行getEntryAfterMiss(key,i,e),源码如(2-2)
(2-2)
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
3.2、如果e!=null,则执行e.get()获取ThreadLocal对象k,
如果k==key,则返回e;
如果k==null,则执行expungeStaleEntry(i);//删除过期的entry对象,【如果key为null,则entry对象为过期对象】
如果k!=null && k!=key,执行i=nextIndex(i,len)则获取tab[ ]数组的下一个索引,
e=tab[i]继续从tab[ ]数组中获取entry对象e。执行3.2操作。
3.3、如果e!=null,则直接获取e.value返回。如果e==null,则执行:setInitialValue();
4、如果 map==null,则执行:setInitialValue();源码(2-3)如下:
(2-3)
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
(2-4)
protected T initialValue() {
return null;
}
4.1、初始化value,执行initialValue()方法,返回null。源码(2-4)
4.2、获取当前线程对象t
4.3、调用getMap(t),获取ThreadLocalMap对象。
4.4、如果map!=null,则执行map.set(this,value);否则,执行createMap(t,value),创建ThreadLocalMap,并且设置key与value。返回value。【value值为null】
三、原理
线程共享变量缓存如下:
Thread.ThreadLocalMap<ThreadLocal, Object>;
1、Thread: 当前线程,可以通过Thread.currentThread()获取。
2、ThreadLocal:我们的static ThreadLocal变量。
3、Object: 当前线程共享变量。
我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<ThreadLocal, Object>,然后根据当前ThreadLocal获取当前线程共享变量Object。
ThreadLocal.set,ThreadLocal.remove实际上是同样的道理。
这种存储结构的好处:
1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。
2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
关于ThreadLocalMap<ThreadLocal, Object>弱引用问题:
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。
虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。
1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。