1、set源码
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取ThreadLocalMap 对象
if (map != null)//判断对象是否为空
map.set(this, value);//map不为null,设置值
else
createMap(t, value);//map为null,创建一个map对象
}
继续深入 ThreadLocalMap map = getMap(t),
我们可以发现ThreadLocal数据隔离的真相,就是每个线程都维护了自己的threadLocal变量,从而实现隔离。
2、ThreadLocalMap的底层源码
static class ThreadLocalMap {
/**
* 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;
}
}
我们可以发现他的数据结构中是Entry中的key是弱引用,value是强引用。
3、map.set的源码
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
从源码里面看到ThreadLocalMap在存储的时候会给每一个ThreadLocal对象一个threadLocalHashCode,在插入过程中,根据ThreadLocal对象的hash值,定位到table中的位置i,int i = key.threadLocalHashCode & (len-1)。
然后进行判断:如果当前位置是空的,就初始化一个Entry对象放在位置i上;
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
如果位置i不为空,如果这个Entry对象的key正好是即将设置的key,那么就刷新Entry中的value;
if (k == key) {
e.value = value;
return;
}
如果位置i的不为空,而且key不等于entry,那就找下一个空位置,直到为空为止。
set原理图:
4、get源码
在get的时候,也会根据ThreadLocal对象的hash值,定位到table中的位置,然后判断该位置Entry对象中的key是否和get的key一致,如果不一致,就判断下一个位置,set和get如果冲突严重的话,效率还是很低的。
public T get() {
Thread t = Thread.currentThread();//当前线程
ThreadLocalMap map = getMap(t);//获得当前线程的ThreadLocalMap对象
if (map != null) {//如果map不为空,代表ThreadLocal有值
ThreadLocalMap.Entry e = map.getEntry(this);//获取键key
if (e != null) {//如果key不为空
@SuppressWarnings("unchecked")
T result = (T)e.value;//获得值
return result;
}
}
return setInitialValue();
}
getEntry()源码:
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);
}
setInitialValue()的源码
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
//get是根据ThreadLocal获取到table的i值,然后查找数据拿到后会对比key是否相等 if (e != null && e.get() == key)。
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;
}
5、造成的问题
由于value是强引用,所以会造成内存泄漏
6、解决
通过remove()方法
提示:本文内容有些图是网络上的,若有侵权,请联系我
我是小白弟弟,一个在互联网行业的小白,立志成为一名架构师
https://blog.csdn.net/zhouhengzhe?t=1