ThreadLocal<T>
实例字段
-
threadLocalHashCode
private final int threadLocalHashCode = nextHashCode();
每个
ThreadLocal
实例的hashCode
。用于将ThreadLocal
变量插入到线程中的一个哈希表中(线程的ThreadLocalMap
字段)
实例方法
protected T initialValue()
:默认实现,返回null
- 一般情况下,当线程第一次调用
ThreadLocal
的get()
时,会调用该方法 - 若线程在先前有调用
set()
方法,则第一次调用get()
时不会调用该方法 - 一般情况下,该方法对每个线程来说,最多只会调用一次
- 但是,在调用
remove()
后再调用get()
,会导致该方法再次被调用 - 通常来说,线程不期望一个
null
的初始值,ThreadLocal
一般会被重写,典型地,会使用一个匿名内部类
- 一般情况下,当线程第一次调用
public T get()
- 首先获取当前线程
Thread t = Thread.currentThread()
- 然后获取这个线程的
ThreadLocalMap
实例map
- 若
map
不为空,以当前ThreadLocal
实例对象为key,去map
中取值,若有值,则直接return
- 若
map
为空,或map
中没值,则return setInitialValue()
- 若
- 首先获取当前线程
private T setInitialValue()
- 先调用
initialValue()
,T value = initialValue()
- 获取当前线程
t
中的ThreadLocalMap
对象map
- 若
map
不为空,以当前ThreadLocal
对象为key,调用map.set(this,value)
- 若
map
为空,调用createMap(t,value)
,为线程t
创建ThreadLocalMap
实例 return value
- 先调用
public void set(T value)
- 获取当前线程
t
的ThreadLocalMap
对象map
- 若
map
不为空,以当前ThreadLocal
对象为key,调用map.set(this,value)
- 若
map
为空,调用createMap(t,value)
,为线程t
创建ThreadLocalMap
实例
- 获取当前线程
public void remove()
- 获取当前线程
t
的ThreadLocalMap
对象map
- 若
map
不为空,调用该map
的remove(this)
方法
- 获取当前线程
静态方法
-
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
return new SuppliedThreadLocal<>(supplier);
返回一个
ThreadLocal
,其初始值由Supplier
来决定 -
private static int nextHashCode()
return nextHashCode.getAndAdd(HASH_INCREMENT);
生成
ThreadLocal
对象的哈希值
静态字段
-
private static AtomicInteger nextHashCode = new AtomicInteger();
用来生成
ThreadLocal
的HashCode -
private static final int HASH_INCREMENT = 0x61c88647;
HashCode生成的增量
静态内部类
ThreadLocalMap
-
静态内部类
Entry
static class Entry extends WeakReference<ThreadLocal<?>>{ Object value; Entry(ThreadLocal<?> k,Object v){ super(k); value = v; } }
-
静态字段
int INITIAL_CAPACITY = 16
-
实例字段
Entry[] table
int size = 0
:Entry数组里的元素个数int threshold
:对Entry数组进行resize的阈值
-
实例方法
-
构造方法
ThreadLocalMap(ThreadLocal<?> firstKey,Object firstValue){ table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY-1); table[i] = new Entry(firstKey,firstValue); size = 1; setThreshold(INITIAL_CAPACITY); //这里会设置负载因子为 2/3 }
-
void setThreshold(int len)
threshold = len * 2 / 3;
-
Entry getEntry(ThreadLocal<?> key)
//获取线程相关的ThreadLocal时,会调用这个方法 //使用Hash来定位该ThreadLocal在线程的map中的位置 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);
-
Entry getEntryAfterMiss(ThreadLocal<?> key,int i,Entry e)
Entry[] tab = table; int len = tab.length; while(e != null){ ThreadLocal<?> k = e.get(); if(k == key) return e; if(k == null) expungeStaleEntry(i); //该Entry不为null,但是该Entry的key,为null //key是一个WeakReference<ThreadLocal<?>> //是会被gc回收的 //而value不是WeakReference //此时应该移除该Entry else i = nextIndex(i,len); //线性探测法,每次+1 //如果当前k不为空,且不等于key,说明发生冲突,则按冲突解决方法向后探测 //探测成功则返回成功结果,否则返回null e = tab[i]; } return null;
-
int expungeStaleEntry(int staleSlot)
Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { tab[i] = null; while (tab[h] != null) h = nextIndex(h, len); ab[h] = e; } } } return i;
-
private void rehash()
expungeStaleEntries(); //先移除所有过时的Entry if(size >= threshold - threshold / 4) //若移除后仍然超过某个阈值 resize(); //扩容
-
private void resize()
扩容为原来的2倍,然后重新Hash
-
private void expungeStaleEntries()
对整个Entry数组进行遍历,依次调用
expungeStaleEntry()
-
总结:
- 核心在于
ThreadLocal
的静态内部类ThreadLocalMap
,在这个ThreadLocalMap
类的内部,维护了一个Entry
数组,这个Entry
数组实际是一个哈希表,Entry
作为ThreadLocalMap
的静态内部类,其结构类似于Map
,一个Entry
对象持有一个ThreadLocal
弱引用,为key,一个Object
,作为value - 在创建一个
ThreadLocal
对象时,会为对象生成一个threadLocalHashCode
,会在插入到线程的ThreadLocalMap
时使用 - 每个线程(
Thread
)内部维护了一个哈希表(ThreadLocalMap
),这个哈希表中存储了该线程的所有线程局部变量(以ThreadLocal
为key,Object
为value) - 在某一线程内调用
ThreadLocal
的set(T t)
方法,会把t
加入到该线程的ThreadLocalMap
中,以当前ThreadLocal
对象为key,t
为value - 获取线程局部变量时,在当前线程中直接调用
ThreadLocal
的get()
方法,即会以当前ThreadLocal
变量为key,去当前线程的map
中寻找对应的value
备注:
- 每个
ThreadLocal
实例对象的hashCode生成规则是不断累加一个数0x61c88647
,这个数有点神奇,不断累加产生的hash值分布非常均匀 - 将
ThreadLocal
对象插入到线程的哈希表中时,将ThreadLocal
实例对象的hashCode与线程的ThreadLocalMap
中的Entry
数组大小做“与”操作,生成该ThreadLocal
在Entry
数组中的位置,插入到Entry
数组中 ThreadLocal
的冲突解决方案采用的是线性探测,每次向后探测一个位置
完