大概介绍
ThreadLocal通俗说法:线程的本地变量,通过为每个线程创建副本的方式解决线程隔离问题,实现线程中的变量传递
比如:
- 一个老项目,要从单线程的改为多线程实现了,那么对于一些变量,不是线程共享的,就要用ThreadLocal包装起来
- 每个线程对应一个请求连接,在这个线程中的多个类、方法都要用到这个请求的用户信息,可见这个信息不是在线程间共享的,那么用ThreadLocal把用户信息包装起来
ThreadLocalMap
- ThreadLocalMap是ThreadLocal的一个内部静态类,Thread类里面有两个ThreadLocal.ThreadLocalMap类型的内部对象,分别为threadLocals、inheritableThreadLocals。threadLocals是线程独占的变量,子线程可以读到父线程inheritableThreadLocals变量的值。
- ThreadLocalMap维护着一堆key是ThreadLocal类型,value随便的Entry。也就是说,Thread类的threadLocals对象有一个内部的Entry[]类型的table变量,table的每一个Entry的key是ThreadLocal对象,value随便。
- ThreadLocal<?>类型的成员是一个弱引用,其特点是,当引用元素无强引用时,JVM GC时会立即回收引用元素。划重点!!!这里,知道弱引用怎么写了吗?!!!!
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
// 初始化ThreadLocalMap
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
方法
public T get();
public void set(T value);
public void remove();
protected T initialValue();
1. initialValue方法
默认的initialValue是返回null,你也可以在创建ThreadLocal变量的时候重写该函数。
// 默认的
protected T initialValue() {
return null;
}
// 重写initialValue函数的实现
private static final ThreadLocal<Map<Charset, CharsetDecoder>> decoders =
new ThreadLocal<Map<Charset, CharsetDecoder>>()
{
@Override
protected Map<Charset, CharsetDecoder> initialValue()
{
return new IdentityHashMap<>();
}
};
2. get方法
首先获取当前的线程,然后获取当前线程的threadLocals,也就是线程独占变量,通过map.getEntry(this)获取Entry,这里的this代表一个ThreadLocal对象,即entry的key,返回这个entry的value。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
3. set方法
这个同get,不解释了
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
4. remove方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
内存泄漏
ThreadLocalMap的key ThreadLocal是弱引用的原因:
- 如果使用强引用,则key永远都不会被回收,生命周期和线程一样
- 使用强引用,一旦key被回收,则就只剩ThreadLocalMap中的弱引用了,会在下次gc的时候被回收。
- 如果key被回收了,那么对应的value没有被回收,为了避免因此造成的内存泄漏,每次get()、set()、remove() ThreadLocalMap中的值的时候,会自动清理key为null的value。
- 一个比较好的做法是,在不需要的时候,手动remove掉。