欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ThreadLocal源码分析

程序员文章站 2022-10-18 17:49:59
ThreadLocal是一种变量类型,根据当前的线程,变量的value也会不一样,也可以理解成每个线程内都有一个变量的副本,很多人说ThreadLocal就是一个key为线程的map,看了下源码,我是这样理解的(可以看下jdk8的官方文档 https://docs.oracle.com/javase/8/docs/api/index.html)public class ThreadLocal { public T get() {...} public void set...

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

 

 

 

 

 

 

 

本文地址:https://blog.csdn.net/Onstduy/article/details/107386004

相关标签: 很细系列 java