首先我们要知道,Thread 类内部持有一个ThreadLocal 的静态内部类ThreadLocalMap对象的引用,默认为null
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
public static void main(String[] args) {
// 定义一个ThreadLocal变量
ThreadLocal<String> thradLocalName = new ThreadLocal<>();
thradLocalName.set("小明");
}
public void set(T value) {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取当前线程的私有容器,即上文提到的threadLocals
ThreadLocalMap map = getMap(t);
// 如果容器不为空, this:上文的thradLocalName对象(引用地址), value:待插入值
if (map != null)
map.set(this, value);
// 容器为空,为当前线程创建一个私有容器并写入数据
else
createMap(t, value);
}
// key:ThreadLocal 对象(地址)
// value:待插入值
private void set(ThreadLocal<?> key, Object value) {
// 数组
Entry[] tab = table;
int len = tab.length;
// 根据key通过一个算法得到其在数组中的索引位置
int i = key.threadLocalHashCode & (len-1);
// 如果这个索引位置数据不为空
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
// 获取这个数据的ThreadLocal对象(引用)
ThreadLocal<?> k = e.get();
// 如果ThreadLocal对象存在,则替换原来的value
if (k == key) {
e.value = value;
return;
}
// 如果ThreadLocal对象不存在(即原来的引用失效),则清除原来的Entry,写入我们的新Entry
// 及时清理无效引用,避免内存泄露问题
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生命周期实际上与thread的生命周期相同。 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。 在使用线程池的情况下,如果没有及时清理ThreadLocal,不仅可能存在内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理