0%

ThreadLocal剖析

文章字数:455,阅读全文大约需要1分钟

ThreadLocal可以在多线程下实现各个线程的数据隔离

存储原理

直接看ThreadLocalget()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}

可以看出

  1. 数据是使用ThreadLocalMap存储的
  2. ThreadLocalMap是存放在线程对象上,所以可以保证线程之间的独立

再看map.getEntry(this)这句话调用的方法

1
2
3
4
5
6
7
8
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);
}

可以看出

  1. 存放在线程的Map通过ThreadLocal对象的threadLocalHashCode获取具体是那个对象
  2. ThreadLocal对象只是获取值的key真正的数据保存在Thread线程对象上

弱引用

存储数据的ThreadLocalMap里可以看到

1
2
3
4
5
6
7
8
9
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
  1. entry的键是被弱引用包裹的,即GC的时候如果没有强引用则会被直接清理。
  2. 即外面的ThreadLocal如果被赋值为null,即取消对象的强引用。不会因为ThreadLocalMap里面还有强引用而无法被清除
  3. 但是value还是强引用,所以如果不remove元素值还是会造成内存泄露

内存泄露的问题

因为只有键有加弱引用,可以使ThreadLocal外界无强引用时直接被GC。但是key还是强引用,所以需要手动remove

其它问题

  1. 虽然ThreadLocal是随着线程消亡,但如果使用线程池,那么就会被复用。因为线程池的原理就是复用线程