简介:ThreadLocal在日常开发中使用的较少,但是在框架应用中起到的作用并不小,例如spring中事务注解实现以及链路追踪数据的传输,ThreadLocal在这过程中起到的作用是什么样的,本文来研究ThreadLocal的结构以及在多线程中的应用。
ThreadLocal的介绍
1. 定义
官方文档介绍:ThreadLocal类可用线程内的局部变量,该变量在多线程情况下通过get或者set访问能够保证线程之间的独立性,能够减少线程在不同函数之间变量传递的复杂度,事务中的连接传递是其比较典型的应用,后续我们会详细介绍。
ThreadLocal内部维护了静态内部类内部类ThreadLocalMap,ThreadLocalMap类中维护了内部类Entry
作为Thread的局部变量,那是用哪个变量维护的呢?查看Thread类的结构可以看到变量
ThreadLocal.ThreadLocalMap threadLocals = null;
2.结构图
ThreadLocal中方法解析
1.initialValue()
//默认返回为空
protected T initialValue() {
return null;
}
2.get()
//获取value值
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程中的变量值threadLocals
ThreadLocalMap map = getMap(t);
//如果 返回的map 不为空
if (map != null) {
//获取key 为当前ThreadLocal的value,类型为Entry
ThreadLocalMap.Entry e = map.getEntry(this);
//不为空返回value值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//map为空或者不存在对应的key,进行初始化赋值,并返回初始值,没有设置初始化值返回null
return setInitialValue();
}
//获取线程中 threadLocals的变量值
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
3. set()
//赋值的操作
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程中 threadLocals变量的值
ThreadLocalMap map = getMap(t);
if (map != null)
//如果map不为空,赋值
map.set(this, value);
else
//为空时,创建默认大小的数组,并赋值
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// ThreadLocal的hash值取模后,获取数组的index
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
通过set()的赋值操作,我们看出存在hash冲突的问题,那是如何解决的呢?
对于hashMap中的hash冲突,使用的是链地址法,将冲突的数据一次存放在单链表中
在ThreadLocalMap采用了开放地址法中线性探测在散列,也就是向后依次查找空闲的位置。
4. remove()
//从ThreadLocalMap中移除当前key为ThreadLocal的值
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
//根据key,清除对应位置的元素
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
使用案例
public class ThreadLocalTest implements Runnable {
public static Number number = new Number(0);
//在多线程中使用时,要保证每个线程持有不同的ThreadLocal对象,这样才能保证线程之间互不影响
public static ThreadLocal<Integer> value = new ThreadLocal<Integer>();
public void run() {
Random r = new Random();
//每个线程计数加随机数,将其存储到ThreadLocal中
value.set(r.nextInt(100));
//输出num值
System.out.println(Thread.currentThread().getName()+"="+value.get());
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new ThreadLocalUnsafe()).start();
}
}
}
输出结果: