图解 ThreadLocal 源码

前言

先来看以下这样一段代码:

public class ThreadLocalDemo {
    static class ThreadA implements Runnable {
        private ThreadLocal<String> threadLocal;

        public ThreadA(ThreadLocal<String> threadLocal) {
            this.threadLocal = threadLocal;
        }

        @Override
        public void run() {
            threadLocal.set("A");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadA输出:" + threadLocal.get());
        }

        static class ThreadB implements Runnable {
            private ThreadLocal<String> threadLocal;

            private ThreadLocal<Integer> keyStore;

            public ThreadB(ThreadLocal<String> threadLocal) {
                this.threadLocal = threadLocal;
                this.keyStore = new ThreadLocal<>();
            }

            @Override
            public void run() {
                threadLocal.set("B");
                keyStore.set(1);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("ThreadB输出:" + threadLocal.get() + keyStore.get());
            }
        }

        public static void main(String[] args) {
            //两个不同的线程操作同一个 ThreadLocal 对象,实际上会
            // 转成对当前线程内部的 ThreadLocalMap 对象进行操作
            ThreadLocal<String> threadLocal = new ThreadLocal<>();
            new Thread(new ThreadA(threadLocal)).start();
            new Thread(new ThreadB(threadLocal)).start();
        }
    }
}

以上我们创建了两个不同的线程,传入的是同一个 ThreadLocal 对象,并且往当中设置了不同的值,最终发现这两个线程中的设置的值是不会相互影响的,那么这是如何做到的呢?线程是如何维护属于自己的变量副本的呢?通过源码分析可以知道其中的原因。

源码分析

ThreadLocal 中 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`对象
}
ThreadLocalMap getMap(Thread t) {
    //获取当前线程的 ThreadLocalMap 成员变量对象,Thread 类当中维护了一个 ThreadLocal.ThreadLocalMap 成员变量
    return t.threadLocals;
}
//当前执行线程的 ThreadLocalMap 为空,则创建一个对象 
void createMap(Thread t, T firstValue) {
     //创建 ThreadLocalMap 对象
     t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// ThreadLocalMap 构造函数
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
     //哈希表的默认大小为16
     table = new Entry[INITIAL_CAPACITY];
     //哈希算法:根据key计算数据应该存在应该在哈希桶数组的哪个位置
     int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
     table[i] = new Entry(firstKey, firstValue);
     size = 1;
     setThreshold(INITIAL_CAPACITY);
}

get 方法:

 //根据当前的ThreadLocal对象(key)获取数据
 public T get() {
        //获取当前执行的线程对象
        Thread t = Thread.currentThread();
        //获取 ThreadLocalMap 对象
        ThreadLocalMap map = getMap(t);
        //如果当前线程的ThreadLocalMap不为空
        if (map != null) {
            //根据键获取 Entry <key,value>对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T) e.value;
                return result;
            }
        }
        //返回初始值
        return setInitialValue();
 }

通过以上的分析,可以画出如下的运行示意图:

结论:执行同一个 ThreadLocal 对象的 get 或 set 方法,本质上是去操作当前执行线程持有的 ThreadLocalMap 对象(当前执行线程(Thread 类)中维护了一个 ThreadLocal.ThreadLocalMap 对象)。 ThreadLocalMap 是一个哈希表,用于存储键值对,可以快速的根据 key 查找出 value,其中,key是当前的 ThreadLocal 对象,value 是我们外部传入的值。

个人博客:https://www.kangpeiqin.cn/#/index
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值