ThreadLocal
多线程中对于解决线程安全的一个操作类
为每个线程都分配一个独立的ThreadLocal对象副本
实现了线程间的数据隔离
//创建ThreadLocal对象,定义对象内资源的类型
ThreadLocal<String> tl = new ThreadLocal<>();
set()//向对象中存入值,每个线程独立
get()//获取对象中的值,每个线程独立
remove()//清除
ThreadLocal实现原理
Thread类
thread类中维护了一个ThreadLocal的内部类ThreadLocalMap对象
ThreadLocalMap对象中维护了一个Entry数组
即每个线程中都维护了一个独立的Entry数组
我们将线程间独立的数据存储在这个Entry数组中
以ThreadLocal对象作为key,value作为值
这样就实现了同一个ThreadLocal对象的数据隔离
ThreadLocal.ThreadLocalMap threadLocals = null;
Set
public void set(T value) {
//获取当前的线程对象
Thread t = Thread.currentThread();
//根据线程对象获取对应的map
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
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的内存泄漏问题
因为ThreadLocalMap中的key是ThreadLocal的弱引用
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;
}
}
}
所以当ThreadLocal对象没有任何的强引用时
(ThreadLocal变量被手动置为空)
,就会在下一次GC时被手动回收
就导致ThreadLocalMap中出现了一个key为null的Entry
无法访问对应的value,同时Entry又不会被GC回收
导致内存泄漏
内存泄漏:
资源不会被程序用到,同时也无法被GC回收
为什么不使用强引用
强引用会导致ThreadLocal对象被回收时,key不会被回收,出现内存泄漏问题
而弱引用虽然也会导致内存泄漏,但可以在下一次set/get/remove时检查nullkey进行清除