Java并发之ThreadLocal

前言

ThreadLocal是一个全局变量,用于解决线程范围内变量共享问题,其实ThreadLocal可以看作是一个Map集合,而key就是当前线程名,而value是想要存放的变量,ThreadLocal的变量用完会自动销毁,不用考虑ThreadLocal中的变量会占用空间,对于ThreadLocal来说每个线程的变量都是独立的,不会相互影响,可以通过ThreadLocal在同一线程内的不同组件传递公共变量

成员变量

我们看ThreadLocal的源码可以看到他有三个成员变量

    /**
    这个属性用于ThreadLocal的桶位寻址
    如果一个threadLocal对象第一次调用get()方法,那么就会给当前线程分配一个value
    这个value和这个threadLocal对象被包装成一个entry
    这个entry的key是threadLocal对象,value是生成的value
    **/
    private final int threadLocalHashCode = nextHashCode();
    /**
    这个属性是创建ThreadLocal会用到该属性
    每创建一个ThreadLocal对象,就会使用该属性分配一个hash值给这个对象
    **/
    private static AtomicInteger nextHashCode = new AtomicInteger();
    /**
    表示hash增量,没创建一个ThreadLocal对象,ThreadLocal.nextHashCode就会增长HASH_INCREMENT
    **/
    private static final int HASH_INCREMENT = 0x61c88647;

get方法

public T get() {
    //获取当前线程
    Thread t = Thread.currentThread();
    //根据当前线程获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    //判断map是否为null
    if (map != null) {
        //当前threadLocal对象作为key获取entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        //判断entry是否为null
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            //返回result
            return result;
        }
    }
    //初始化
    return setInitialValue();
}

接下来我们再看这个初始化方法

setInitialValue方法

这个方法是用于初始化的,我们从上面的get方法可以知道,进入该方法有两种情况,一个就是ThreadLocalMap为null或者Entry为null

    private T setInitialValue() {
        //调用ThreadLocal对象的方法进行初始化
        T value = initialValue();
        //获取到当前线程
        Thread t = Thread.currentThread();
        //当前线程作为key获取ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //判断map是否为null,如果不为null证明已经初始化过ThreadLocalMap对象了(一个ThreadLocal只会初始化一次)
        if (map != null)
            //向ThreadLocalMap中保存当前threadLocal与当前线程生成的线程局部变量
            map.set(this, value);
        else
            //如果走到这里证明map==null,进行初始化创建一个ThreadLocalMap对象
            createMap(t, value);
        return value;
    }

set方法

public void set(T value) {
    //获取当前线程
    Thread t = Thread.currentThread();
    //将当前线程作为key获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    //判断threadLocalMap是否为null
    if (map != null)
        //覆盖或者添加新的key - value
        map.set(this, value);
    else
        //证明还没有被初始化,进行初始化
        createMap(t, value);
}

如何实现线程隔离?

每一个Thread都有着自己的ThreadLocalMap用于存储数据,当线程访问某个ThreadLocal对象的get方法的时候,方法内部会判断该线程的ThreadLocalMap数组内是否存在以当前线程为key的Entry节点,如果数组内没有对应的节点,那么当前ThreadLocal对象,就会调用初始化方法,创建一个Entry节点放入ThreadLocalMap中

ThreadLocal内存泄漏原因

主要是因为ThreadLocalMap的Entry对象是弱引用,对于弱引用,JVM在每次GC的时候都会清理他们,此时key就会变为null,如果此时没有其他强引用指向value,那么此时value就不会被访问到,按道理来说value应该也会被GC,但是此时ThreadLocalMap的Entry对象对value是强引用,这样就会导致JVM始终无法GC value,这样就导致了value成了一个始终无法清理的value

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值