1. ThreadLocal是什么?
线程变量,属于当前线程,该变量对其他线程是隔离的,为变量在每个线程中都创建了一个副本,每个线程都可以访问自己内部的副本变量
使用场景:
1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
2、线程间数据隔离
3、进行事务操作,用于存储线程事务信息。
4、数据库连接,Session会话管理。
2. 怎么用
public static void main(String[] args) {
//新建一个ThreadLocal
ThreadLocal<String> local=new ThreadLocal<>();
// 新建一个随机数类
Random random=new Random();
// 使用java8Stream 新建5个线程
IntStream.range(0,5).forEach(a->new Thread(()->{
//为每个线程设置相应的local值
local.set(a+" "+random.nextInt(10));
System.out.println("线程和local值分别是"+ local.get());
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
}).start());
}
3. 源码分析
set方法
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 将线程作为key存到ThreadLocalMap 中
ThreadLocalMap map = getMap(t);
// 如果map不存在则初始化一个
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap 是ThreadLocalMap的内部类
其内部还包含一个静态类
static class ThreadLocalMap {
// 继承的弱引用,在Entry内部使用ThreadLocal作为key,用我们设置的threadLocal值作为value
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 还有其他方法
// 调用当前线程 t 返回当前线程中的成员变量ThreadLocals,而ThreadLocals其实就是ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
}
ThreadLocal.ThreadLocalMap threadLocals = null;
get方法
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 用getMap方法获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果map不为空使用当前线程作为Entry的key然后值就是相对应的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 如果没有就设置初始值
return setInitialValue();
}
// 设置初始值
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
remove方法
// 从map中移除即可
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
总结
- 每个Thread维护着一个ThreadLocalMap的引用
- ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
- ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
- ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
- 在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。
- ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
4. 内存泄露
1、Thread中有一个map,就是ThreadLocalMap
2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。
3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收
4、重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。