ThreadLocal介绍

ThreadLocal

1.定义

Java中,ThreadLocal用于创建线程局部变量,即每个线程都有自己独立的变量副本,每个线程都可以独立的访问自己的副本,互不干扰。一般存储线程的私有数据。
ThreadLocal类中主要方法包括:
set(T value);设置当前线程的局部变量值
get();获取当前线程的局部变量值
remove();移除当前线程的局部变量值

2.部分源码分析

public static void main(String[] args) {
        ThreadLocal<String> tl = new ThreadLocal<>();
        tl.set("string");
}

实例化ThreadLocal的时候,可以指定泛型,set(T value)方法传入的就是指定泛型类型的值
set(T value)的源码如下:

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,这个ThreadLocalMap是什么呢?继续看这个getMap的源码

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

发现其实拿的就是当前线程的一个属性

ThreadLocal.ThreadLocalMap threadLocals = null;

那我们继续看下这个当前线程的这个ThreadLocalMap的源代码

static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
	}

看到了什么,Entry对象继承了一个弱引用对象?

  1. 先说下Entry的基本概念

Entry其实是Map接口里面的一个内部接口,他表示Map里面的一个键值对(key-value pair)。在java里面,Map表示键值对的集合,Entry表示单个键值对的实体。
那ThreadLocalMap中的Entry呢?其实也是键值对,只不过是ThreadLocalMap自定义的内部类。

  1. 再来说一下弱引用

弱引用描述的是有用但非必须的一些对象,当这个对象只被弱引用指向的时候,即便是内存足够的情况下,这个对象也会立即被垃圾回收器回收。在Java中使用WeakReference类来实现弱引用,WeakReference类包装了一个对象的弱引用,可以通过get()方法获取被弱引用指向的对象。当被弱引用指向的对象没有强引用时,垃圾回收器会回收该对象,并且get()方法会返回null。

举个栗子

Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj);

这段代码怎么指向的呢?有三个指向如下:

  1. weakRef 强强的指向了 WeakReference对象
  2. WeakReference的泛型变量T reference 弱弱的指向了Object对象
  3. obj强强的指向了Object对象

然后我们进行下面的操作

obj = null; // 原始对象不再有强引用

此时会发生什么?

// 在某个时刻,可能会被垃圾回收器回收
Object retrievedObj = weakRef.get();
if (retrievedObj == null) {
    System.out.println("Original object has been garbage collected");
}

好了,弱引用应该差不多了了解了,言归正传

  1. 我们结合上面的知识,再来看看ThreadLocalMap的源代码

Entry类继承了弱引用WeakReference类,这个WeakReference类包装了ThreadLocal的弱引用,然后来看下Entry的构造函数,super(k);
k是什么,还记得吗?也是ThreadLocal类型的参数啊;说明这个参数现在被弱弱的引用了。

继续回到set(T value);源码

map.set(this, value);这个this是什么,这个this就是ThreadLocal对象,所以这个Map的key就是ThreadLocal对象。
所以是不是这个ThreadLocal对象被两个引用指向了,一个强(new ThreadLocal),一个弱。

  • 问题1:有人会问为什么不能都强强的引用?

举个例子啊,我现在把tl == null,会发生什么,ThreadLocal对象强引用没了,只剩下弱引用了,那么就可以被垃圾回收期回收了,如果是强引用,那不就是内存泄露了吗?

  • 这时候,又有人问,那ThreadLocalMap中的Entry对象仍然存在怎么办呢?

当ThreadLocalMap在进行操作时会检查Entry对象的key是否为null,如果为null,则会清理掉这个Entry对象。以便释放被回收的ThreadLocal对象所占用的内存。这种设计可以帮助避免内存泄漏,因为即使ThreadLocal对象被回收,Entry对象也会被及时清理掉。

但是呢,如果这个线程生命周期很长,这个ThreadLocal对象一直被频繁占用,可能会导致大量的数据堆积在ThreadLocalMap中,直到线程结束。

如果使用完ThreadLocal,我们没有调用remove()方法手动清理,这些值也会一直存在,直到在某个适当的时候被清理,也就是上面说的ThreadLocalMap操作的时候会检查key为null的数据

  • 那又有人问,我每次都调用remove()不就好了,还设计什么弱引用?

那emm就是框架的设计肯定需要考虑多个方面,实际开发会存在遗漏吗,就需要一个健壮强大的框架。

  • 34
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值