本文目录
ThreadLocal是什么
下面引用ThreadLocal的API注释
这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其get或set方法)都有自己独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。
例如,下面的类生成每个线程本地的唯一标识符。 线程的ID在第一次调用ThreadId.get()时被分配,并在后续调用中保持不变。
只要线程存活并且ThreadLocal实例可以访问,每个线程都保存对其线程局部变量副本的隐式引用; 线程消失后,线程本地实例的所有副本都将被垃圾收集(除非存在对这些副本的其他引用)。
ThreadLocal原理
ThreadLocal供线程局部变量。实际上它并不存放任何的信息。简单的说,ThreadLocalMap是ThreadLocal真正的数据存储容器,而ThreadLocalMap实例是Thread的成员变量,如果在同一个线程中使用多个ThreadLocal实例,实际上,每个ThreadLocal实例对应的是ThreadLocalMap的哈希表中的一个哈希槽。
下面画个图说明一下在线程中使用ThreadLocal实例的:
线程实例和ThreadLocal实例的关系如下:
ThreadLocal实例总是通过Thread.currentThread()获取到当前操作线程实例,然后去操作线程实例中的ThreadLocalMap类型的成员变量,因此它是一个桥梁,本身不具备存储功能。
ThreadLocal源码分析
ThreadLocal结构
ThreadLocal构造方法
- ThreadLocal只有一个无参构造方法,初始化的时候什么也不做。可以猜到在第一次set方法调用的时候会懒初始化一个ThreadLocalMap
/**
* 自定义哈希码(仅在 ThreadLocalMaps 中有用)
* 消除冲突在连续构造 ThreadLocals 的常见情况下被相同的线程使用
*/
private final int threadLocalHashCode = nextHashCode();
/**
* 原子计数器,给出的下一个哈希码
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* //哈希魔数
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* 创建一个线程局部变量
*
*/
public ThreadLocal() {
}
ThreadLocal内部类ThreadLocalMap结构
- 在下面的代码中,我们可以看出,当前ThreadLocal的引用k被传递给WeakReference的构造函数,所以ThreadLocalMap中的key为ThreadLocal的弱引用。当一个线程调用ThreadLocal的set方法设置变量的时候,当前线程的ThreadLocalMap就会存放一个记录,这个记录的key值为ThreadLocal的弱引用,value就是通过set设置的值。如果当前线程一直存在且没有调用该ThreadLocal的remove方法,当没有强引用来引用ThreadLocal实例的时候,JVM的GC会回收ThreadLocalMap中的这些Key,此时,ThreadLocalMap中会出现一些Key为null,但是Value不为null的Entry项,这些Entry项如果不主动清理,就会一直驻留在ThreadLocalMap中。
/**
* ThreadLocalMap是一个定制的散列映射,仅适用于维护线程本地变量。
* 它的所有方法都是定义在ThreadLocal类之内。
* 它是包私有的,所以在Thread类中可以定义ThreadLocalMap作为变量。
* 为了处理非常大(指的是值)和长时间的用途,哈希表的Key使用了弱引用(WeakReferences)。
* 引用的队列(弱引用)不再被使用的时候,对应的过期的条目就能通过主动删除移出哈希表。
*/
static class ThreadLocalMap {
/**
* Entry的Key为WeakReference<ThreadLocal<?>>
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/**
* 这里是正在存放ThreadLocal.set()的值.
*/
Object value;
/**
* k:ThreadLocal的引用,被传递给WeakReference的构造方法
*/
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* 初始容量,必须是2的幂
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 哈希(Entry)表,必须时扩容,长度必须为2的幂次方
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private ThreadLocalMap.Entry[] table;
/**
* 哈希表中元素(Entry)的个数
* The number of entries in the table.
*/
private int size = 0;
/**
* 下一次需要扩容的阈值,默认值为0
* The next size value at which to resize.
*/
private int threshold; // Default to 0
/**
* 设置下一次需要扩容的阈值,设置值为输入值len的三分之二
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* 以len为模增加i
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* 以len为模减少i
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
}
TreadLocal的set方法
public void set(T value) {
//获取当前线程实例
Thread t = Thread.currentThread();
//从当前线程实例中获取threadLocals属性
ThreadLocalMap map = getMap(t);
if (map != null)
//threadLocals属性不为null则覆盖key为当前的ThreadLocal实例,值为value
map.set(this, value);
else
//threadLocals属性为null,则创建ThreadLocalMap,第一个项的Key为当前的ThreadLocal实例,值为value
createMap(t, value);
}
- 在上面的代码中,处调用getMap方法获得当前线程对应的threadLocals
//获取ThreadLocalMap实例时候总是从线程实例的成员变量获取
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
- 如果getMap方法返回null说明是第一次调用set方法(前面说到过,threadLocals默认值为null,只有调用set方法的时候才会创建map),这个时候就需要调用createMap方法创建threadLocals,该方法如下所示
// 创建ThreadLocalMap实例的时候,会把新实例赋值到线程实例的threadLocals成员
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
TreadLocal的get方法
- 在get方法的实现中,首先获取当前线程实例,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null。
public T get() {
//获取当前线程实例
Thread t = Thread.currentThread();
//获取当前线程的threadLocals变量
ThreadLocalMap map = getMap(t);
//如果threadLocals变量不为null,就可以在map中查找到本地变量的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量
return setInitialValue();
}
private T setInitialValue() {
//protected T initialValue() {return null;}
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//以当前线程作为key值,去查找对应的线程变量,找到对应的map
ThreadLocalMap map = getMap(t);
//如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值
if (map != null)
map.set(this, value);
//如果map为null,说明首次添加,需要首先创建出对应的map
else
createMap(t, value);
return value;
}
TreadLocal的remove方法
- remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量
public void remove() {
//获取当前线程绑定的threadLocals
ThreadLocalMap m = getMap(Thread.currentThread());
//如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量
if (m != null)
m.remove(this);
}
总结
完
感谢您的阅读
如果你发现了错误的地方,可以在留言区提出来,我对其加以修改