ThreadLocal
当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal
简单来说,就是操作线程中的ThreadLocalMap ,ThreadLocalMap 中包含了一个Entry数据用来存放多个(threadLoacl和value的键值对)
Thread 类中包含一个ThreadLocalMap类
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal 中有一个initialValue() setInitialValue() set(T value)方法,get()方法和remove()方法
initialValue()就是返回初始值,可以重写来实现自己的初始值
setInitialValue() 是设置初始值并返回初始值
set(T value)方法 为当前线程的threadLocals中设置value值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //return t.threadLocals;
if (map != null)
map.set(this, value); //为此此线程的ThreadLocalMap 中设置一个threadLoacl和value的键值对
else
createMap(t, value);
}
get() 从当前线程的ThreadLocalMap 中的Entry数组中获取 此threadlocal的值
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();
}
remove()
从当前线程的ThreadLocalMap 中的Entry数组中把这个threadlocal和value的键值对置为null
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) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap使用ThreadLocal的弱引用作为key,
如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,
这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的
Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话(线程池重用线程),
这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,
造成内存泄漏。
ThreadLocal的措施
get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value
但无法保证
static的ThreadLocal,延长了ThreadLocal的生命周期,可能导致的内存泄漏
分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么就会导致内存泄漏