ThreadLocal用法和原理

今天看shiro相关的内容,读到了关于ThreadLocal的代码。感觉跟之前的理解有点差别,记录一下,先把看懂的记录一下。

ThreadLocal最常用的两个方法:set(T value), T get()

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
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();
}

首先看set方法: 

1:首先获取当前线程 Thread t = Thread.currentThread();

2:然后调用getMap(t) 获取ThreadLocalMap,这个ThreadLocalMap直接从上面获取的线程中拿,代码如下:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals; // 注意,这里操作的是当前线程的ThreadLocalMap 
}

3:然后判断后去的ThreadLocalMap是否为空,如果不为空,保存信息。set的时候第一个参数是自己,第二个参数是要保存的内容,所以最主要的还是看ThreadLocalMap的set方法,进入set方法:

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

    /** 可以看到这里是用了一个Entry数组来保存数据,之前不明白这里为啥要用数组,来保存。后来想想一个线程有多个ThreadLocal,
        每个ThreadLocal做set操作的时候都会操作的都是当前线程的ThreadLocalMap。并且set的时候用ThreadLocal的地址作为
        key,保证当前线程中ThreadLocalMap的key不会重复
    */
    Entry[] tab = table;  
    int len = tab.length;

    // 这里用当前对象获取hashcode与数组长度做 与 运算,得出一个值,并且这个值不会超过数组的长度
    int i = key.threadLocalHashCode & (len-1);
    // 首先根据上面与操作获取的值去取数据,我认为是为了提高效率吧,也不知道对不对
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
 
        // 如果key值相等,将原值覆盖
        if (k == key) {
            e.value = value;
            return;
        }
        // TODO 下面这个暂时没看懂,看懂再更新
        if (k == null) {
            replaceStaleEntry(key, value, i);
            return;
        }
    }

    // 大意是上面的循环没有获取到相等的key,然后下面需要新增一个entry
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        rehash();
}

get() 方法

public T get() {
    // 首先还是获取当前线程
    Thread t = Thread.currentThread();
    // 返回当前线程的ThreadLocalMap 引用
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 调用ThreadLocalMap的getEntry方法获取entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            T result = (T)e.value;
            return result;
        }
    }
    //setInitialValue()方法,如果在使用ThreadLocal之前没有set值,直接调用get会返回null
    return setInitialValue();
}

 

private Entry getEntry(ThreadLocal<?> key) {
    // 当前key的hashcode与数组长度做 与 运算,得到一个下标
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    // entry不为空并且key相等的话,返回值。因为上面set值的时候,下标的获取也是和上面一样。我暂且认为是为了提高效率吧,毕竟        不用一个一个迭代了
    if (e != null && e.get() == key)
        return e;
    else
        // 如果为空或者key值不相等的话,迭代整个table
        return getEntryAfterMiss(key, i, e);
}

 

到此还有两个方法没搞懂,搞懂了再来更新,哇咔咔:

get值的时候有个:

Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e)

set值的时候有个:

replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot)

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值