源码分析ThreadLocal

ThreadLocal是一种变量类型,根据当前的线程,变量的value也会不一样,也可以理解成每个线程内都有一个变量的副本,很多人说ThreadLocal就是一个key为线程的map,看了下源码,我是这样理解的(可以看下jdk8的官方文档 https://docs.oracle.com/javase/8/docs/api/index.html

public class ThreadLocal<T> {
    public T get() {...}
    public void set(T value) {...}
    }

说map的大体上没问题,但是Threadlocal考虑的显然是比map全面且高效

public T get() {
    // 获取当前的线程
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //map不为空,把当前的ThreadLocal变量作为key,从map获取entry节点
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // map为空时,获取初始值
    return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
    // threadlocal 内部方法,看得出thread本身有一个threadLocalMap类型的成员变量
    return t.threadLocals;
}

注意,是Thread类内有ThreadLocal.ThreadLocalMap threadLocals,ThreadLocalMap是ThreadLocal的内部类,下面是他们的层级结构,

map里有一个Entry类型的数组table,Entry节点继承WeakReference弱引用

public class ThreadLocal<T>{
    ...
    static class ThreadLocalMap {
        private Entry[] table;
        static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
        Object value;
    
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
        }
    }

}

贴一段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的threadLocalHashCode 去拿到数组下标,判断是否为预期的key,如果发生hash碰撞,那就执行getEntryAfterMiss,这个源码不贴了,就是一个递归拿值。

简单说下set,其实也是一个从设置map key-value的过程

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

代码简洁,有map就set,没有就先创建

private void set(ThreadLocal<?> key, Object value) {
    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();
}

这段代码其实很好理解,比hashmap的set简单,根据key的hashcode计算下标,处理hash碰撞

再补充一个点,说下ThreadLocalMap中的Entry为什么继承了WeakReference?

java中一共有四种引用,强软弱虚,WeakReference就是里面的弱引用。对引用不懂的可以看下这篇文章

https://www.cnblogs.com/czx1/p/10665327.html

这种引用的特点就是,弱引用的对象生命周期短,Gc碰到就回收,回收之后通过引用获取的值是null,跟强引用不同在于,比如一个userInfo,userinfo=null,会把userinfo设置为null,但是不会回收,直到线程结束才会回收整个map;弱引用不同,通过ThreadLocal.remove()同时会释放map中的entry,清除彻底;userinfo=null只释放掉了userinfo,没有释放entry

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值