1.使用场景及示例
ThreadLocal的作用:
- ThreadLocal通过为每一个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。
- 传递数据:可以通过ThreadLocal在
同一线程
的不同组件中传递公共变量。 - 线程隔离:每个线程的变量都是独立的,不会相互影响。
代码示例
package myJUC;
import java.util.concurrent.TimeUnit;
public class ThreadLocalTest {
private static ThreadLocal<Node> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
//调用set方法设置值 设置val = 1
threadLocal.set(new Node(1));
System.out.println("线程A获取到的值为 :" + threadLocal.get().val);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A获取到的值为 :" + threadLocal.get().val);
System.out.println("线程A获取到的值为 :" + threadLocal.get().val);
System.out.println("线程A获取到的值为 :" + threadLocal.get().val);
}).start();
new Thread(() -> {
//调用set()方法 设置val = 2
threadLocal.set(new Node(2));
//调用get()
System.out.println("线程B获取到的值为 :" + threadLocal.get().val);
System.out.println("线程B获取到的值为 :" + threadLocal.get().val);
System.out.println("线程B获取到的值为 :" + threadLocal.get().val);
}).start();
}
}
class Node {
int val;
public Node(int val) {
this.val = val;
}
}
/* 运行结果
线程B获取到的值为 :2
线程B获取到的值为 :2
线程B获取到的值为 :2
线程A获取到的值为 :1
线程A获取到的值为 :1
线程A获取到的值为 :1
线程A获取到的值为 :1
*/
/*
* 从运行结果可以发现,不同的线程的数据是隔离的,一个线程只能获取本线程设置到ThreadLocal
* 中的数据。
*/
2.内存结构示意图
3.内部结构
public class ThreadLocal<T> {
/*
* 线程获取threadLocal.get()时,如果是第一次在某个ThreadLocal对象上get时,会给当前
* 线程分配一个value,这个value 和 当前的threadLocal对象被包装称为一个entry,
* 其中key是threadLocal对象,value是threadLocal对象给当前线程生成的value,
* --- Entry<ThreadLocal, Value> ---
* 这个entry存放到当前线程threadLocals这个map的哪个桶位? 与当前threadLocal对象的
* ThreadLocalHashCode有关,使用threadLocalHashCode & (table.length - 1)得到
* 的索引位置,就是当前entry需要存放到位置。
*/
private final int threadLocalHashCode = nextHashCode();
/*
* 创建ThreadLocal对象时,会使用到,每创建一个threadLocal对象,就会使用
* nextHashCode 分配一个hash值给这个对象。
*/
private static AtomicInteger nextHashCode = new AtomicInteger();
/*
* 每为线程创建一个ThreadLocalMap对象,这个ThreadLocal.nextHashCode这个值就会增 * 长,这个值很特殊,它是斐波那契数。hash增量为这个数字,带来的好处就是hash分布非常均 * 匀
*/
private static final int HASH_INCREMENT = 0x61c88647;
/*
* 创建ThreadLocal对象时会使用到,每创建一个ThreadLocalMap对象,就会使用 * nextHashCode 分配一个hash值给这个对象。
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
/*
* 需要重写
*/
protected T initialValue() {
return null;
}
//构造方法
public ThreadLocal() {
}
4.get方法()
//-----------------------ThreadLocal.get()------------------------------------
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程内部的threadLocals。
ThreadLocalMap map = getMap(t);
//map不为NULL,说明当前线程已经拥有自己的ThreadLocalMap对象了。
if (map != null) {
/*
* ThreadLocalMap的Entry的key就是ThreadLocal,所以这里传入 * this(ThreadLocal对象)尝试获取当前ThreadLoacl关联的Entry.
*/
ThreadLocalMap.Entry e = map.getEntry(this);
//e不为NULL,说明当前线程初始化过了与当前ThreadLocal对象相关联的线程局部变量
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
//直接返回对应的value即可。
return result;
}
}
/*
* 执行到这里有几种情况?
* 1.当前线程对应的threadLoaclMap为NULL,
* 2.当前线程与当前threadLocal对象没有生成过相关联的线程局部变量
*/
//setInitialValue() 初始化当亲线程与当前threadLocal对象相关联的value。
//且当前线程内部的threadLoaclMap没有创建的话,还会初始化创建Map。
return setInitialValue();
}
//----------------------------ThreadLocal.getMap()----------------------------
/**
* threadLocals是Thread类内部的一个属性,
* (ThreadLocal.ThreadLocalMap threadLocals;)
* 可以理解为就是一个Map<ThreadLocal, Value>
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
4.1setInitialValue()
// ---------------------------ThreadLocal.setInitialValue()-------------------
private T setInitialValue() {
/*
* 调用当前ThreadLocal对象的initialValue()方法,这个方法,大部分情况我们都会 * 重写,value就是当前ThreadLocal对象与当前线程相关联的线程局部变量。
*/
T value = initialValue();
//获取当前线程对象
Thread t = Thread.currentThread();
//获取线程内部的threadLocalMap对象
ThreadLocalMap map = getMap(t);
//map不为NULLL,说明当前线程内部的threadLocalMap已经初始化过了
if (map != null)
//保存当前threadLocal与当前线程生成的线程局部变量
// key ——> 当前threadLocal对象 value -> value
map.set(this, value);
else
/*
* 执行到这里说明当前线程内部的threadLocalMap对象还未创建
* 执行createMap()方法。
*
* 参数一:当前线程 参数二:value。
*/
createMap(t, value);
return value;
}
// --------------------------ThreadLocal.createMap()--------------------------
void createMap(Thread t, T firstValue) {
/*
* 为线程t内部的 threadLocals赋值 创建一个ThreadLocalMap对象
* this : 当前threadLoca对象
* firstValue : value。
*/
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//-------------------------ThreadLoacalMap.构造方法------------------------
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化底层的Entry数组
table = new Entry[INITIAL_CAPACITY];
//调用threadLocalHashCode计算哈希值并寻址
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//在寻址后的位置上构造一个Entry。
table[i] = new Entry(firstKey, firstValue);
//size设置为1
size = 1;
//设置阈值(后面详细讲)
setThreshold(INITIAL_CAPACITY);
}
4.2get()方法运行流程图
由图可知,调用get时如果当前线程内部的map未被创建或者map中没有当前threadLocal对象对应的Entry,那么最终也会创建出Map并且插入相应的Entry.
5.set()方法解析
public void set(T value) {
//获取当前线程以及内部的threadLocals(threadLocalMap)
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//条件成立:说明当前线程的threadLocalMap已经初始化了
if (map != null)
//替换value
map.set(this, value);
else
//创建线程内部的threadLocalMap并设置value。
createMap(t, value);
}
5.1set()方法运行流程
6.remove()方法
将当前线程内部的ThreadLocalMap中key为当前threadLocal对象的Entry干掉!!!(具体源码见ThreadLocal源码解析之ThreadLocalMap解析)
//--------------------ThreadLocal.remove()-----------------------------------
public void remove() {
//获取当前线程内部的ThreadLocalMap对象,
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//调用remove方法。(key = 当前threadLocal对象)
m.remove(this);
}