ThreadLocal源码分析

1、简介java

  1.   咱们知道多个线程同时读写同一共享变量存在并发问题,为此咱们能够突破共享变量,没有共享变量就不会有并发问题,可使用局部变量。正所谓没有共享,就没有伤害,本质上就是避免共享,除了局部变量,Java语言提供的线程本地存储(ThreadLocal)就能作到。


       其实每一个线程内部都会维护一个ThreadLocalMap属性,每份线程独自的数据都存放在ThreadLocalMap中Entry[] table属性里,Entry对象的key就是ThreadLocalvalue就是本身设置的值,若是程序里有多个ThreadLoca属性,每一个线程在运行时会将用到ThreadLocal,生成Entry保存到table中,有点须要注意的是同一个Entry中value从新设置会被替换,如Entry<ThreadLocal,value>中value被设置成value2,变成Entry<ThreadLocal,value2>,和Map相似,若是想保存多个值,能够将value封装成对象。数组

2、属性bash

//ThreadLocal中的hash值,目的是为了定位存放在ThreadLocalMap中Entry[] table的那个位置
private final int threadLocalHashCode = nextHashCode();
//原子类,对hashCode进行累加HASH_INCREMENT
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;复制代码

3、内部类并发

一、SuppliedThreadLocalide

//目的是为了在初始化调用ThreadLocal.get()方法时,能根据传入的函数式接口supplier的get方法获取默认值
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {    
    private final Supplier<? extends T> supplier;    
    SuppliedThreadLocal(Supplier<? extends T> supplier) 
    {        
        this.supplier = Objects.requireNonNull(supplier);    
    }   
     @Override 
    protected T initialValue() {        
    return supplier.get();    
    }
}复制代码

二、ThreadLocalMap函数

//每一个线程Thread,内部都维护了一个ThreadLocal.ThreadLocalMap threadLocals = null属性,每一个线程的//数据都各自保存在各自的ThreadLocalMap的table属性中,table中的每一个元素都是Entry<ThreadLocal,Value>,
//每一个ThreadLocal,都会做为Entry的key,好比有个Test类,内部有两个ThreadLocal,若是A线程在运行时,使用到两个ThreadLocal,那线程A中的ThreadLocalMap中的table[Entry0<ThreadLocal,value>,Entry1<ThreadLocal1,value1>]
static class ThreadLocalMap {    
    static class Entry extends WeakReference<ThreadLocal<?>> {        
        Object value;        
        Entry(ThreadLocal<?> k, Object v) {            
           super(k);            
           value = v;        
       }    
   }
    //table初始容量 
     private static final int INITIAL_CAPACITY = 16;
    //保存每一个线程中的使用到的ThreadLocal做为key的多个Entry 
    private Entry[] table;    
    //table中的元素
    private int size = 0;
    //扩容的阈值 
    private int threshold; 复制代码

4、构造ThreadLocal对象ui

  1. 静态方法withInitial构造

    //传入函数式接口,构造ThreadLocal的静态内部类子类SuppliedThreadLocal,ThreadLocal.get()还未设置值时获取null,SuppliedThreadLocal.get()获取的值是传入的函数式接口supplier的get方法的值
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {    
         return new SuppliedThreadLocal<>(supplier);
    }复制代码

  2. 构造函数构造

    public ThreadLocal() {}复制代码

5、get方法this

public T get() {
    //获取当前执行的线程 
    Thread t = Thread.currentThread();
    //从Thread中获取threadLocals属性,即Thread中的ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocalMap map = getMap(t);//看下面的getMap方法
    //若是当前线程的ThreadLocalMap不为null
    if (map != null) {
        //getEntry看下面介绍的ThreadLocalMap的getEntry方法 
        ThreadLocalMap.Entry e = map.getEntry(this);        
        if (e != null) {            
            @SuppressWarnings("unchecked")            
            T result = (T)e.value;            
            return result;        
       }    
    }    
    return setInitialValue();}复制代码

getMap方法,从Thread中获取threadLocal属性spa

ThreadLocalMap getMap(Thread t) {    
    return t.threadLocals;
}复制代码

setInitialValue方法,初次构造ThreadLocalMap对象,并设置firstValue线程

private T setInitialValue() {
    //若是是经过构造函数构造ThreadLocal,initialValue()方法返回null,若是是经过withInitial静态方法构造ThreadLocal,initialValue()方法返回supplier.get();
    T value = initialValue();
    //获取当前线程 
    Thread t = Thread.currentThread();
    //从Thread中获取threadLocal属性 
    ThreadLocalMap map = getMap(t);    
    if (map != null)
        //若是map不为null,将其value设置进去,key为当前的ThreadLocal,set会在下面set方法中介绍 
        map.set(this, value);   
     else
        //构造ThreadLocalMap,而且将其ThreadLocalMap赋值给当前线程的threadLocals属性 
        createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, firstValue);
    return value;}复制代码

ThreadLocalMap构造方法

  1. 公共的构造方法

    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
                //建立个大小为16的Entry数组
                table = new Entry[INITIAL_CAPACITY];
                //根据ThreadLocal的threadLocalHashCode属性值&上table数组长度-1,计算出该ThreadLocal的Entry的存放位置
                int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                //将其Entry存入数组中,Entry继承于弱应用,这样的话当ThreadLocal被赋值为null,gc能够回收掉ThreadLocal,而不至于Entry对ThreadLocal的引用,致使gc回收不了,防止内存泄漏 
                table[i] = new Entry(firstKey, firstValue);    
                //table数组中存在的元素
                size = 1;
                //设置扩容的阈值大小 threshold = len * 2 / 3 table数组长度乘于2除以3 
                setThreshold(INITIAL_CAPACITY);
    }复制代码

  2. 私有的构造方法,

    //线程间的InheritableThreadLocal传递,这个会在详细介绍InheritableThreadLocal中介绍
    private ThreadLocalMap(ThreadLocalMap parentMap) {
        //父线程的ThreadLocalMap 
        Entry[] parentTable = parentMap.table;
        //长度 
        int len = parentTable.length;
        //设置当前线程的阈值 
        setThreshold(len);
        //建立大小和父线程相同的Entry数组 
        table = new Entry[len];
        //将其父线程中的ThreadLocalMap中Entry和Entry的key(ThreadLocal)不为空的Entry的元素添加到子线程的Entry的table中 
        for (int j = 0; j < len; j++) {        
            Entry e = parentTable[j];        
            if (e != null) {            
                @SuppressWarnings("unchecked")            
                ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();            
                if (key != null) {
                    //childValue()方法只有在ThreadLocalMap的子类InheritableThreadLocal中才能使用,不然在ThreadLocal中调用会报UnsupportedOperationException异常
                    Object value = key.childValue(e.value);//return parentValue
                    Entry c = new Entry(key, value);                
                    //计算Entry的存放位置
                    int h = key.threadLocalHashCode & (len - 1);                
                    //若是当前位置有存在,则存放下一个位置,只到下一个位置没有元素
                    while (table[h] != null)                    
                        h = nextIndex(h, len);                
                    table[h] = c;
                    //当前线程的Entry的table数组的元素大小加1 
                    size++;            
                }        
            }    
        }
    }复制代码

       createInheritedMap方法,调用ThreadLocalMap的私有构造方法

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {    
    return new ThreadLocalMap(parentMap);
}复制代码

      createInheritedMap方法的在哪里被调用

//Thread类中的init方法
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
    //.....省略n行代码,inheritThreadLocals默认为true,若是建立当前线程的父线程inheritableThreadLocal不为空,将其inheritableThreadLocals的ThreadLocalMap中的Entry的table数组赋值给当前线程 //好比在main线程中建立了test线程,而且main中的属性inheritableThreadLocals不为空,会将其inheritableThreadLocals中的的ThreadLocalMap中的Entry的table数组赋值给test线程,inheritableThreadLocals属性的类只能是InheritableThreadLocal
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);       
}复制代码

ThreadLocalMap的getEntry方法

private Entry getEntry(ThreadLocal<?> key) {
    //ThreadLocal的threadLocalHashCode & 上 ThreadLocalMap的Entry数组table的长度-1,定位元素所在的下标 
    int i = key.threadLocalHashCode & (table.length - 1);    
    Entry e = table[i];
    //entry不等于null而且e.get和当前的ThreadLocal相等,返回entry 
    if (e != null && e.get() == key)        
        return e;    
    else
        //若是entry等于null,获取e.get()和当前的ThreadLocal不相等 
        return getEntryAfterMiss(key, i, e);}复制代码

ThreadLocalMap的getEntryAfterMiss方法

private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
    //ThreadLocalMap的Entry数组table 
    Entry[] tab = table;
    //数组长度 
    int len = tab.length;
    //若是Entry e为null,直接返回空,e不为空,找到key和当前ThreadLocal相等的Entry,不然只到遍历完table中的size 
    while (e != null) {
        //获取从entry中获取key 
        ThreadLocal<?> k = e.get();
        //若是key和当前的ThreadLocal相等,则直接返回entry 
        if (k == key)            
            return e;
        //若是当前的entry的key为null,即外部将其ThreadLocal的属性赋值为null,获取被gc回收 
        if (k == null) 
            //清除当前key为null的Entry,而且从新计算后面的每一个Entry元素的存放位置,说白了就是全部元素的位置都向前移动 
            expungeStaleEntry(i);        
        else 
            //获取下一个ThreadLocal为key的Entry 
            i = nextIndex(i, len);        
        e = tab[i];    
    }    
    return null;
}复制代码

ThreadLocalMap的expungeStaleEntry方法

private int expungeStaleEntry(int staleSlot) {    //当前线程的ThreadLocalMap的Entry数组table属性
    Entry[] tab = table;
    //table数组的长度 
    int len = tab.length;
    //将其当前key为null的entry的value也设置为null,加快value的gc回收 
     tab[staleSlot].value = null;
    //将其整个entry设置为null 
     tab[staleSlot] = null;
    //table数组中存在的元素减1 
     size--;    
     Entry e;    
     int i;
    //从当前key为null的Entry的存放位置statleSlot位置开始日后面开始找到key为null的entry而且清除掉,不然的话,从新计算key不为null的Entry元素的存放位置 
      for (i = nextIndex(staleSlot, len); (e = tab[i]) != null;i = nextIndex(i, len)) {
          ThreadLocal<?> k = e.get();
          //当前位置的entry的key为null,清除value和entry,而且table数组元素减1 
          if (k == null) {            
             e.value = null;            
             tab[i] = null;            
             size--;        
          } else {
            //从新计算当前位置的entry的存放位置 
            int h = k.threadLocalHashCode & (len - 1);            
            if (h != i) {
                //将之前位置的entry置为null 
                tab[i] = null;
                //从新找到新的存放位置 
                while (tab[h] != null)                    
                   h = nextIndex(h, len);                
                tab[h] = e;            
            }        
         }    
     }
    //返回当前操做过的i 
    return i;
}


复制代码

6、set方法

public void set(T value) {
    //获取当前线程 
    Thread t = Thread.currentThread();
    //从当前线程中获取ThreadLocalMap 
    ThreadLocalMap map = getMap(t);    
    if (map != null)        
        map.set(this, value);    
    else
        //若是ThreadLocalMap不存在,构造ThreadLocalMap,而且将其ThreadLocalMap赋值给当前线程的threadLocals属性 ,ThreadLocalMap的构造函数能够看上面的介绍
        createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, firstValue); 
}复制代码

ThreadLocalMap的set(this,value)方法

private void set(ThreadLocal<?> key, Object value) {    
    Entry[] tab = table;    
    int len = tab.length;    
    int i = key.threadLocalHashCode & (len-1);
    //从当前算出来的位置i从后开始遍历 
    for (Entry e = tab[i]; e != null;e = tab[i = nextIndex(i, len)]) {        
        ThreadLocal<?> k = e.get();
        //寻找到k和当前的ThreadLocal相等,替换值,为此一个ThreadLocal只能存放线程的一个值 
        if (k == key) {
            //将当前值设置到value中 
            e.value = value;            
            return;        
        }
        //若是在遍历的过程当中,存在某一项key为null的Entry 
        if (k == null) {            
            //替换元素,而且作一些清除和从新计算元素的存放位置的操做,能够看下面replaceStaleEntry方法的介绍
            replaceStaleEntry(key, value, i);            
            return;        
        }    
    }
    //当前位置i上本来就没元素,或者在上面的遍历中,没有找到相同的key,或者某一项key的值为null的entry替换,而是从遍历中找到位置上没有元素的i 
    tab[i] = new Entry(key, value);
    //table的元素加1 
    int sz = ++size;
    //若是没有清除一些entry(key为null),而且ThreadLocalMap中的元素个数大于阈值,则对table进行扩容 
    if (!cleanSomeSlots(i, sz) && sz >= threshold)        
    rehash();
}

复制代码

replaceStaleEntry方法

private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) {    
    Entry[] tab = table;    
    int len = tab.length;    
    Entry e;
    //将key为null的entry的位置staleSlot赋值给新的变量 
    int slotToExpunge = staleSlot;
    //从slotToExpunge位置向前遍历,找到比此值更前的key为null的entry的在table中的下标 
    for (int i = prevIndex(staleSlot, len);(e = tab[i]) != null;i = prevIndex(i, len))
        //若是在遍历前面table元素的时候找到key为null的entry,对slotToExpunge的值进行从新赋值 
        if (e.get() == null)            
            slotToExpunge = i;
    //从当前的staleSlot向后遍历 
    for (int i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {        
        ThreadLocal<?> k = e.get();
        //若是后面找到和当前ThreadLocal相等key的Entry 
        if (k == key) {
            //赋予新值 
            e.value = value;
            将参数传进来的staleSlot(key为null的entry下标)赋值给当前ThreadLocal的Entry的所在位置,说白了就是两个互换位置            
            tab[i] = tab[staleSlot];            
            tab[staleSlot] = e;
            //若是前面没有找到比当前staleSlot更前的key为null的Entry位置,将当前i赋值给slotToExpunge 
             if (slotToExpunge == staleSlot)                
                 slotToExpunge = i;
            //cleanSomeSlots方法能够看下面的cleanSomeSlots介绍,expungeStaleEntry方法能够看上面expungeStaleEntry方法的介绍 
            cleanSomeSlots(expungeStaleEntry(slotToExpunge),len);            return;        }
        //找到后面第一项key为null的entry的下标i,赋值给slotToExpunge,目的是为了后面执行expungeStaleEntry方法,清除key为null的entry,而且从新移动后面元素的存储位置 
        if (k == null && slotToExpunge == staleSlot)            
            slotToExpunge = i;    
    }
    //将其当前传进来key为null的entry的value置空 
    tab[staleSlot].value = null;
    //从新建立Entry对象存入当前位置 
    tab[staleSlot] = new Entry(key, value);
    //若是slotToExpunge和staleSlot不相等,说明上面有找到其余key为null的entry 
    if (slotToExpunge != staleSlot)
        //cleanSomeSlots和expungeStaleEntry配合使用,expungeStaleEntry清除后面key为null的entry和对key不为null的entry从新向前移动存储位置,cleanSomeSlots是为了清除前半部分key为null的entry数据 
        cleanSomeSlots(expungeStaleEntry(slotToExpunge),len);}复制代码

cleanSomeSlots方法

private boolean cleanSomeSlots(int i, int n) {    
    boolean removed = false;    
    Entry[] tab = table;    
    int len = tab.length;    
    do { 
        //获取table中的下一个元素 
        i = nextIndex(i, len);        
        Entry e = tab[i];
        //若是当前entry不为null,key为null 
        if (e != null && e.get() == null) {            
            n = len;            
            removed = true;
            //调用上面介绍的expungeStaleEntry方法,寻找后面的key为null的entry,将值和entry置为空,而且对后面key不为null的entry从新赋值到新位置 
            i = expungeStaleEntry(i);        
        }    
    } while ( (n >>>= 1) != 0);    
    return removed;
}复制代码


7、remove方法

public void remove() {
    //从当前线程中获取ThreadLocalMap 
    ThreadLocalMap m = getMap(Thread.currentThread());
    //若是当前的ThreadLocalMap不为null,调用下面的那个remove方法,说明若是当前线程没有设置ThreadLocal.ThreadLocalMap threadLocals = null属性,调用remove没影响
    if (m != null)        
        m.remove(this);
}

private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            //从当前位置开始日后找,找到key和当前ThreadLocal相等的Entry
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    //Entry元素的key设置为null
                    e.clear();
                    //从当前位置开始查询key为null的entry,和从新计算后面存在的entry元素的位置,看上面expungeStaleEntry方法的介绍
                    expungeStaleEntry(i);
                    return;
                }
            }
        }复制代码

有不清楚的地方,能够留言一块儿探讨

相关文章
相关标签/搜索