ThreadLocal
注意点
-
线程本地变量
即每个线程对象都维护了自己的ThreadLocalMap,只访问自己的Map,所以是安全的。
Thread类里面有ThreadLocal.ThreadLocalMap属性,threadLocals和inheritableThreadLocals,以此保证每个线程都维护一份自己的数据
-
内存泄露
ThreadLocalMap的Entry[] table中Entry继承了WeakReference,其中Key即ThreadLocal对象是弱引用,是为了避免内存泄露。 为什么能避免呢? 如下
已知存在
Thread->ThreadLocalMap->Entry->Key(ThreadLocal)
的引用联关系。假设ThreadLocalMap.Entry中的ThreadLocal不设置成弱引用,那么当你在一个方法体内大量创建ThreadLocal并进行set操作之后,即使你该方法执行完了,创建的ThreadLocal对象引用也没有逃逸出该方法,但是它永远GC不掉,因为任然有开头说的Thread这边的强引用,所以这种无用对象一直堆积而得不到GC,就产生了内存泄露。不过一般我们都不会在方法体内创建ThreadLocal,一般是private static ThreadLocal创建的。所以也不会泄露问题。
image引用链关系图
-
ThreadLocalMap哈希冲突
set方法时候处理hash冲突的方式是调用nextIndex(index, length)方法,其实就是在当前index基础上+1
-
流程
-
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
new ThreadLocal<>()是空构造器,只不过会初始化实例变量,threadLocalHashCode
-
threadLocal.set("nmm")
public void set(T value) {
//获取当前线程的ThreadLocalMap对象
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//存在则用该ThreadLocal作为Key,设置进去
if (map != null)
map.set(this, value);
else
//不存在则创建map,同时设置value
createMap(t, value);
}
3. `threadLocal.get("nmm")`
public T get() {
//获取当前线程绑定的map
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//map中查找该ThreadLocal的entry,返回值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
4. `threadLocal.remove()`主要就调用ThreadLocalMap.remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
private void remove(ThreadLocal<?> key) {
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)]) {
if (e.get() == key) {
//把Reference的referent关联的ThreadLocal 清除
e.clear();
//清理map中key为null的value,跟weakHashMap一样
expungeStaleEntry(i);
return;
}
}
}