ThreadLocal在解读

26 篇文章 0 订阅

ThreadLocal在解读

ThreadLocal 字面意思是本地线程

源码里给的说明大概如下:

  • 这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问每个线程(通过其getset方法)都有自己独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。

  • 只要线程存活并且ThreadLocal实例可以访问,每个线程都保存对其线程局部变量副本的隐式引用; 线程消失后,线程本地实例的所有副本都将被垃圾收集(除非存在对这些副本的其他引用)。

ThreadLocal主要的三大方法和一个内部静态类:get 、set 、remove、ThreadLocalMap,接下来让我们一起根据源码进行分析一下。

ThreadLocalMap

由于这三大方法都用到了ThreadLocalMap,我们先来阅读一下ThreadLocalMap的源码

官方说明大意:ThreadLocalMap是一个定制的哈希映射,仅适用于维护线程本地值。在ThreadLocal类之外不导出任何操作。类是包私有的,允许在类线程中声明字段。为了帮助处理非常大和长寿命的用法,哈希表条目使用weakreference作为键。但是,由于不使用引用队列,因此只有当表开始耗尽空间时,才能保证删除过时的条目。

    static class ThreadLocalMap {
		//此哈希映射中的条目扩展WeakReference,使用它的主ref字段作为键(始终是ThreadLocal对象)。
        //注意,空键(即entry.get()==null)意味着不再引用该键,
        //因此可以从表中展开该项。在下面的代码中,这些条目被称为“过时的条目”。
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        //构造一个最初包含(firstKey,firstValue)的新映射。
		//ThreadLocalMaps是延迟构建的,因此我们只有在至少有一个条目要放入其中时才创建一个。
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //这里也可以看出 ThreadLocalMap 底层是维护了一个Entry数组
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

ThreadLocalMap 初步先说到这里,主要是让大家先明有个初步的认识,在下面将方法时不至于过于迷茫,在下面也会进一步讲解。

set 方法

首先让我们来看一下set的源码

    public void set(T value) {
        //获取访问的线程
        Thread t = Thread.currentThread();
        //根据访问的线程确定是否存在
        ThreadLocalMap map = getMap(t);
        //存在就直接替换value,否则创建
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

	private void set(ThreadLocal<?> key, Object value) {
//我们不像get()那样使用快速路径,因为使用set()创建新条目至少和替换现有条目一样常见,在这种情况下,快速路径失败的频率更高。
        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();
			//根据key替换value
            if (k == key) {
                e.value = value;
                return;
            }
			//替换就的entry
            if (k == null) {
                replaceStaleEntry(key, value, i);
                return;
            }
        }
		//创建一个新的
        tab[i] = new Entry(key, value);
        int sz = ++size;
        //清理
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    };

get

理解了set方法,get方法就很容易了

//ThreadLocal中get方法
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();
}
    
//ThreadLocalMap中getEntry方法
private Entry getEntry(ThreadLocal<?> key) {
       int i = key.threadLocalHashCode & (table.length - 1);
       Entry e = table[i];
       if (e != null && e.get() == key)
            return e;
       else
            return getEntryAfterMiss(key, i, e);
   }
ThreadLocal特性

ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是

  • Synchronized是通过线程等待,牺牲时间来解决访问冲突
  • ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杨同学~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值