1、简介java
其实每一个线程内部都会维护一个ThreadLocalMap属性,每份线程独自的数据都存放在ThreadLocalMap中Entry[] table属性里,Entry对象的key就是ThreadLocal,value就是本身设置的值,若是程序里有多个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
//传入函数式接口,构造ThreadLocal的静态内部类子类SuppliedThreadLocal,ThreadLocal.get()还未设置值时获取null,SuppliedThreadLocal.get()获取的值是传入的函数式接口supplier的get方法的值
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}复制代码
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构造方法
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);
}复制代码
//线程间的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;
}
}
}复制代码
有不清楚的地方,能够留言一块儿探讨