关于ThreadLocal存取数据的流程分析

关于ThreadLocal存取数据的流程分析

一、关于存数据ThreadLocal.set()

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

关于ThreadLocal.getMap()方法:

ThreadLocalMap getMap(Thread t) {    
    return t.threadLocals;
}

Thread.getMap()直接返回当前线程的threadlcoals属性

① 首先获取当前线程对象t

② 调用getMap()方法来获得t的threadlocals属性(可见threadlcoals是每个线程都有的属性)

ThreadLocalMap的声明如下:默认值为空

ThreadLocal.ThreadLocalMap threadLocals = null;

③ 判断获得的ThreadLocaMap对象是否为空。

​ 如果不为空,表示不是第一次调用ThreadLocal.set()方法,直接调用ThreadLocalMap.set()方法,设置值。

​ 如果为空,则代表是第一次调用ThreadLocal.set()方法,就调用ThreadLocal.creatTable()方法。

ThreadLocal.creatTable()方法:

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

直接new一个ThreadLocalMap对象(这个类是ThreadLocal的一个静态内部类)赋给当前线程对象的threadlocals属性。

new ThreadLocalMap()构造方法的源码:

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {    	table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    //可以看到传入的ThreadLocal对象和firstValue被封装到Entry对象,并放到了table[]中
    table[i] = new Entry(firstKey, firstValue);
    size = 1;                                                           setThreshold(INITIAL_CAPACITY);
}

关于table[]:它是ThreadLocal的静态内部类ThreadMap的一个属性:

private Entry[] table;

二、关于取数据ThreadLocal.get()

ThreadLocal.get()的源码如下:

public T get() {
    //获取当前线程的threadlocals属性
    Thread t = Thread.currentThread();    
    ThreadLocalMap map = getMap(t);    
    if (map != null) {
        //如果map不为null,执行getEntry()方法,以this(调用该方法的LocalThread对象)为key,获取对应的Entry对象
        ThreadLocalMap.Entry e = map.getEntry(this);        
        if (e != null) {            
            //获取到的Entry对象不是null,获取它的值返回
            @SuppressWarnings("unchecked")            
            T result = (T)e.value;            
            return result;        
        }    
    }
    //没有获取到map对象,或是当前的ThreadLocal对象在Entry[](ThreadLocalMap的table属性)没有与key和它相同,就调用初始化值方法。
    return setInitialValue();
}

三、设计理由

可以看到上面的流程比较复杂,为什么不能直接向Thread类中的ThreadLocalMap对象存取数据呢?这是不能实现的,看一下threadLocals的声明:

(缺省:包级访问)ThreadLocal.ThreadLocalMap threadLocals = null;

变量threadLocals是包级访问,所以不能从外部直接访问该变量,只有同名的包中的类可以访问threadLocals变量,而ThreadLocal和Thread恰好在一个包中,可以访问。(其实这样设计也是为了只能让ThreadLocal来访问,ThreadLocalMap的一个内部类它的一个实例也是Thread的一个属性,外部想要操作这个这个属性是行不通的,只有通过ThreadLocal来操作这个属性,不如说为了只让ThreadLocal来操作才是设计包级访问的原因,只能让ThreadLocal来操作也是为了确保这是线程私有的数据,否则只是普通的设计一个Map,那么其他的线程也可以获取并修改数据)。

同一个ThreadLocal可以存在于多个Thread对象的theadLocals属性中作为key值,但在不同的线程中通过其对象来获取的值有各个线程所决定。这是因为么个线程对象都有一个ThreadLocalMap对象。这样其他线程即使获取了ThreadLocal对象也只能修改自己的数据。注意,线程的Map中的key必须是不同ThreadLocal对象,否则会覆盖。

ThreadLocal存取数据的操作都要先获取当前对象 ,就是为了确保数据的线程私有性,其他线程不能操作除自己以外的其他线程的数据,这也是ThreadLocal的初衷。将ThreadLocalMap设为ThreadLocal的内部类也是为了对于ThreadLocalMap的操作只对ThreadLocal可见(可以看到ThreadLocalMap的方法基本全是private的,只对外部类ThreadLocal可见)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值