ThreadLocal
可以在多线程下实现各个线程的数据隔离
存储原理
直接看ThreadLocal
的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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看出
- 数据是使用
ThreadLocalMap
存储的 -
ThreadLocalMap
是存放在线程对象上,所以可以保证线程之间的独立
再看map.getEntry(this)
这句话调用的方法
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);
}
可以看出
- 存放在线程的
Map
通过ThreadLocal
对象的threadLocalHashCode
获取具体是那个对象 -
ThreadLocal
对象只是获取值的key
真正的数据保存在Thread
线程对象上
弱引用
存储数据的ThreadLocalMap
里可以看到
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
-
entry
的键是被弱引用包裹的,即GC
的时候如果没有强引用则会被直接清理。 - 即外面的
ThreadLocal
如果被赋值为null
,即取消对象的强引用。不会因为ThreadLocalMap
里面还有强引用而无法被清除 - 但是
value
还是强引用,所以如果不remove
元素值还是会造成内存泄露
内存泄露的问题
因为只有键有加弱引用,可以使ThreadLocal
外界无强引用时直接被GC
。但是key
还是强引用,所以需要手动remove
其它问题
- 虽然
ThreadLocal
是随着线程消亡,但如果使用线程池,那么就会被复用。因为线程池的原理就是复用线程
网友评论