Java并发之ThreadLocal源码分析(第三篇:获取元素)

前言

    get()方法用于获取当前线程中以ThreadLocal对象为Key对象的线程局部变量

 

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();  
    } 

用于获取线程局部变量的方法

1、获取当前线程对象

首先得到调用该get方法的Thread对象,并由临时变量t负责存储

2、获取当前Thread对象持有的ThreadLocalMap对象

接着将Thread对象t传入一个getMap()方法中,该方法会返回一个Thread对象持有的ThreadLocalMap对象,接着由局部变量map负责临时存储ThreadLocalMap对象

3、检查Therad对象是否持有一个ThreadLocalMap对象

若Thread对象持有的ThreadLocalMap对象未创建,则getMap()方法会返回null,所以做出以下判断

a、当map得到的是null时,说明当前Thread对象持有的ThreadLocalMap对象还未创建,则会调用一个setInitialValue()方法,setInitialValue()方法的返回值最终成为get()方法的返回值

b、当map获取到当前Thread对象持有的ThreadLocalMap对象时,则先调用map的getEntry()方法获取一个对象,getEntry()方法接受一个当前的ThreadLocal对象,该调用将会返回一个ThreadLocalMap.Entry对象或者代表没有匹配元素的null,返回的对象将由临时变量e持有,所以这里对两种情况均做了处理

第一:e得到的是null,此时则会走到最下方的setInitialValue()方法中(见3号知识点),setInitialValue的返回值将会作为get方法的最终返回值(返回的是线程局部变量对象)

第二:若e不是null,就取出ThreadLocal.Entry对象e持有的一个Object对象value,然后将value向下转型为实际类型T,再赋值给局部变量result进行存储,最后则会返回result保存的对象,该result保存的就是我们存储的线程局部变量对象(也称线程全局变量,在本线程内角度看的时候)

 

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()方法会被调用

第一:获取线程局部变量对象时,用于存储key-value的ThreadLocalMap对象还未创建时,该方法会被调用

第二:获取线程局部变量对象时,在当前Thread对象持有的ThreadLocalMap对象中,通过ThreadLocal对象作为key,没有查找到匹配的ThreadLocalMap.Entry对象,该方法会被调用

在该方法内部,首先会调用initialValue方法(见5号知识点),该方法用于返回一个作为默认的线程局部变量对象,随后会将该值交由局部变量value进行持有,接着调用Thread的静态方法currentThread(),获得当前访问该方法的Thread对象,并由局部变量t持有,然后会把当前Thread对象t传入到getMap方法中(见1号知识点),该方法返回的ThreadLocalMap对象由局部变量map进行存储,map的值有两种情况

第一种情况:map指向的对象已经创建,此时map不为null,马上调用它的set方法(),set方法(见6号知识点)是哈希表插入元素的方法,接受两个参数,一个就是当前的ThreadLocal对象,另一个就是通过initialValue方法(见5号知识点)得到的默认局部局部变量对象

第二种情况:map为null,此时则会调用一个createMap方法(见7号知识点),同样也是把当前ThreadLocal对象作为key,默认的Value对象作为value

最后该方法会返回线程局部变量对象value,而返回值value对象则会成为get方法(见0号知识点)的返回值

 

 

getEntry()方法分析(此方法位于ThreadLocalMap类中)

        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对象作为key,首先获得key的hashCode值,该值正是由ThreadLocal对象持有的threadLocalHashCode实例变量保存着,接着将获得key的hashCode值与(底层数组长度-1)进行一个按位与运算,计算出的值正是桶的地址(哈希地址),该值交由临时变量i进行存储,将桶的下标i传入到底层数组对象table中得到的Entry对象,由变量e进行保管。

熟悉的代码告诉我,ThreadLocalMap是哈希表结构,它的底层数组容量也一定是2的n次方,只有这样按位与运算才等同于取模运算!(详情见HashMap)

 

set()方法分析(注意:这是ThreadLocalMap的set()方法,不是TheradLocal的set()方法)

        private void set(ThreadLocal<?> key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            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();
        }

TheradLocalMap的set()方法用于添加元素,此处省略了……将在ThreadLocalMap中分析

 

总结

1、Thread对象持有的ThreadLocalMap对象,它是一个哈希表结构的对象,在ThreadLocalMap持有的数组对象中,存储着以ThreadLocal对象为Key对象,线程局部变量对象为Value对象的ThreadLocal.Entry对象

2、每个Thread对象持有的ThreadLocalMap对象threadLocals,是在ThreadLocal类中的createMap()方法中创建的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值