当涉及多线程的时候,ThreadLocal是经常会被使用到的,所以我们来看看ThreadLocal到底是什么
ThreadLocal是一个线程内部的存储类,他能够为每个线程存储数据,而且每个线程存储的数据在线程相互之间是独立的。要取到某一线程的存储数据就必须切换到某一线程才能将其数据取出。这就像是ThreadLocal为线程开辟了各自独立的存储空间来存储线程的局部变量。所以,当一个变量需要被多线程使用到并且修改的时候,就会用到ThreadLocal了,因为如果不使用ThreadLocal的话,多个线程对变量进行了修改,那么变量的值只会是最后一个线程修改的值,其他线程都只会拿到这个值,那么这样的多线程就无意义了。
ThreadLocal的使用及原理
static final ThreadLocal<T> threadLocals = new ThreadLocal<>();
threadLocals.set(T类型的XXX);
threadLocals.get();
set
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取ThreadLocalMap,线程一开始都是默认为null,所以走else,createMap方法
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//每个thread中都维护了一个ThreadLocalMap
}
void createMap(Thread t, T firstValue) {
//实例化一个ThreadLcalMap,并将其赋给线程的成员变量threadLocals
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap是Thread类的静态内部类
private static final int INITIAL_CAPACITY = 16;
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];//new一个长度为16的Entry数组
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//将线程哈希,计算出放到哪个下标
table[i] = new Entry(firstKey, firstValue);//实例化Entry存储线程和值,并将其放入到对应下标的Entry数组中。
size = 1;//存储的线程数大小为1
setThreshold(INITIAL_CAPACITY);//设置阈值
}
private void setThreshold(int len) {
threshold = len * 2 / 3;//这里相当于hashMap的0.75的负载因子,只要超过这个threshold值就需要扩容
}
get
public T get() {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取当前线程的ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//Entry是ThreadLocalMap的一个静态内部类,是对ThreadLocal的弱引用,同时让ThreadLocal和储值形成key-value的形式
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//如果当前线程的ThreadLocalMap是null的话执行该方法
}
private T setInitialValue() {
//该initialValue方法固定返回的是Null,若编程者没有调用set设置值之后才调用get,而是直接调用get,
//那么在这里通过createMap方法创建的ThreadLocalMap,也就是当前线程的ThreadLocalMap里保存的Entry的键值对将会是----->ThreadLocal对象 : Null
//并且最后会返回Null回去,不会报错
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
通过上图我们知道,每个thread线程都会有成员变量 threadLocals 其类型是 ThreadLocalMap,这个变量主要通过其内部静态类Entry来将threadLocal对象和value值绑定形成键值关系,就相当于hashMap一样,threadLocal :value--------> key : value, 若通过threadlocal的set一直赋值则会将前面所赋的值覆盖掉,最后取出的将会是最后赋的值。