浅谈一下ThreadLocal

虽然说每个线程访问到的ThreadLocal对象是同一个,但是在同一个ThreadLocal对象中会给每一个线程维护自己单独的ThreadLocalMap,然后ThreadLocalMap中会维护一个Entry数组,然后将当前ThreadLocal对象的弱引用作为key,需要存储的值作为value。存到Entry中,为什么可以保证线程安全呢,就是因为每个线程的ThreadLocalMap都不一样,所以在进行get、set时操作的Entry数组也不一样,自然就做到了不同线程直接的隔离,从而保证了线程安全。

需要注意:每个ThreadLocal只能set一个对象,Entry数组存储的不是同一个ThreadLocal对象set的不同值,而是存储的不同的ThreadLocal对象set的值。

ThreadLocal中有四个方法:

  • initialValue():就是返回一个初始值null
  • get():首先会拿到当前线程,然后根据当前线程去拿到对应的ThreadLocalMap。ThreadLocalMap中会维护一个Entry类型的数组,Entry其实就是一个key-value形式的数据结构。如果不为空,则根据当前的ThreadLocal对象计算对应的哈希值后去Entry数组中拿到对应的Entry对象,然后将里面存储的value强转为泛型T然后返回。如果Entry中value为空或者map为空,都会调用setInitialValue()方法进行初始化,初始化一个map并且设置value为null进行返回。
    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();
    }

    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;
    }
  • set():同样,先根据当前线程拿到对应的ThreadLocalMap对象,如果map为null则创建一个map并存储对应的Entry对象,如果存在map,则进行set操作,具体的set流程是这样的:先获取到Entry数组,并计算当前key的索引,然后遍历Entry进行查找,然后找到了对应的Entry对象则更新value并返回,如果没找到(可能是已经过期或者被回收)则重新创建一个Entry对象并插入数组中。
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }


        private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
  • remove():先获取当前线程的ThreadLocalMap,然后计算需要remove掉的Entry的索引,然后清除掉对应的Entry后对Entry数组进行一些必要的调整
        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值