ThreadLocal提供线程局部变量,这些变量与正常的变量不一样,每一个线程访问ThreadLocal实例的时候都有自己的,独立初始化的变量副本.ThreadLocal实例通常是类中的私有静态字段.
实现每一个线程都有自己专属的本地变量副本 (自己用自己的变量,不和其他线程共享),避免了线程问题.
protect T initialValue() 返回此线程局部变量的当前线程的初始值,通常使用匿名内部类,不推荐使用
为每个线程设置初始值
ThreadLocal<Integer> saleVolume = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
为每个线程设置初始值 推荐使用这种方式对每个线程进行初始化赋值
ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
设置当前线程的变量副本值
获取当前线程的变量副本值
删除此线程局部变量的当前线程值
注:必须回收自定义的ThreadLocal变量,尤其是在线程池的场景下,线程经常会被复用,如果不清理自定义的ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄漏问题
问题解决:Thread ThreadLocal ThreadLocalMap之间的关系
线程类中包含了ThreadLocal ,ThreadLocal 中包含了ThreadLocalMap静态内部类
ThreadLocal是一个泛型类,保证可以接受任何类型的对象,ThreadLocal中维护了一个Map的静态内部类,我们使用set,get方法其实都是这个ThreadLocalMap类中对应的get,set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
最终的变量放在了线程的ThreadLocalMap中,并不是存在ThreadLocal上,ThreadLocal作为key
强引用:无论任何情况下,只要强引用关系还存在,垃圾回收器就不会回收该对象,例如Object o = new Object();这种引用关系为强引用
软引用:在将发生内存溢出之前,将会把这些对象列为回收范围,垃圾回收时并且达到内存溢出情况会将这些对象进行回收
弱引用:在将发生垃圾回收时,不管会不会发生内存溢出,都会将此类对象进行垃圾回收
虚引用:表示已经回收的对象,为对象设置虚引用关联的唯一目的就是在这个对象被垃圾收集器时收到一个系统通知
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal的外部强引用为null了,此时ThreadLocal的外部强引用一定会被垃圾回收掉
ThreadLocal<String> s = new ThreadLocal<>();
s.set("hello");
s.get();
s.set()方法建立一个entry节点,假设方法执行完,栈帧被销毁,s也就没有了,但是此时ThreadLocalMap中的entry中的key引用还指向这个对象,如果这个key为强引用,就会导致key指向ThreadLocal对象不能被垃圾回收,造成内存泄漏
这个key为弱引用就大概率的减少内存泄漏问题,ThreadLocal对象在方法执行完毕后顺利被回收且Entry的key引用指向就为null
当我们往ThreadLocal变量赋值,实际上就是当前的Entry(ThreadLocal为key,value为值)往ThreadLocalMap中存放,Entry中的key是弱引用,当threadLocal外部引用被置为null,当GC的时候,这个ThreadLocal就会被回收,这时key为null,value还存在,就没有办法访问这些key为null的Entry值,如果线程不结束,这时存在很多key为null的entry,value无法回收,造成内存回收(线程复用 线程池)
调用get,set,remove方法时,就会尝试删除key为null的entry,可以是否value对象占用的内存
ThreadLocal通常用static关键字修饰,ThreadLocal能实现线程隔离,不在于它自己本身,而是在于Thread的ThreadLocalMap,所以,ThreadLocal可以只初始化一次,只分配一块存储空间,没必要作为成员变量多次被初始化