一、ThreadLocal作用
1. 线程间隔离,每个线程都拥有自己独立的对象
2. 在线程生命周期内可以获取到该对象
二、ThreadLocal优点
1. 线程安全
2. 不需要加锁,提高执行效率
3. 高效利用内存,节省开销
4. 避免繁琐的传参
三、两种使用方式:
1. 创建ThreadLocal时,可以控制的对象
ThreadLocal<SimpleDateFormat> threadLocal2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"))
2. 创建ThreadLocal时,没法控制对象
ThreadLocal<User> threadLocal2 = new ThreadLocal<>();
threadLocal2.set(user);
四、源码
涉及到的相关类:Thread,ThreadLocal,ThreadLocalMap
ThreadLocal.java
1. T initialValue()
默认实现返回空
a. 该方法会返回当前线程对应的初始值,这是一个延迟加载的方法,只有在调用get()的时候,才会触发
b. 当线程第一次调用get()方法访问变量时,将调用此方法,除非线程先调用了set方法,在这种情况下,不会为线程调用本initialValue方法。即:如果先调用了set()方法,则initialValue方法将不会再被调用
2. void set(T t)
为线程设置一个新值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
3. T get()
得到线程对应的value,如果是首次调用get(),则会调用initialize来得到这个值.
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
- 获取当前ThreadLocal对应的entry
- 获取entry中的value
4. void remove()
删除线程对应的value
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null) {
m.remove(this);
}
}
备注:map中的key和value都是保存在线程中的,而不是保存在ThreadLocal中
ThreadLocalMap.java
每个线程Thread类里面的成员变量,最重要的一个属性 Entry[] table,可以认为是一个map,键值对:
- 键:这个ThreadLocal
- 值:实际需要的变量,比如user或者simpleDateFormat对象
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
六、使用ThreadLocal注意事项
1. 内存泄漏 OOM
- ThreadLocalMap的每个Entry都是一个对key的弱引用,同时,每个Entry都包含一个对value的强引用
- 正常情况下,当线程终止,保存在ThreadLocal里的value会被gc回收,因为没有任何强引用
- 若线程不终止,那么key对应的value就不能被回收,因为有引用关系: Thread->ThreadLocalMap->Entry(key为null)-> Value
- jdk保护机制,在set,remove,rehash方法中会扫描key为null的entry,并把对应的value设置为null
2. 调用remove避免内存泄漏