一、概念
ThreadLocal:线程本地变量,以空间换时间的方式为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。可以理解为一个当前线程的map对象,key为当前ThreadLocal,value为存放的对象。
二、原理
1.set():ThreadLocal为当前线程存储对象方法,设置当前线程对应的线程局部变量ThreadLocal.ThreadLocalMap threadLocals的值。如果不存在则用创建一个,然后将hreadLocal为key,存放对象为value的ThreadLocalMap类型对象赋值给当前线程的threadLocals属性。
public void set(T value) {
//当前线程
Thread t = Thread.currentThread();
//创建ThreadLocalMap getMap(t)为返回当前此线程有的threadLocals值
//threadLocals 为当前线程的 成员变量 类型为ThreadLocal.ThreadLocalMap
//ThreadLocalMap 是一个类 内含一个类型为 Entry<k,v>结构对象 k 为当前ThreadLocal对象 v 为 要存储对象
//就是返回当前线程的 一个 类型为 ThreadLocalMap 的属性
ThreadLocalMap map = getMap(t);
if (map != null)
//不为空 以当前的ThreadLocal对象 为 key ,要存储对象 为 value
//当前线程.threadLocals = <ThreadLocal,要存储对象>
map.set(this, value);
else
//为空 创建以当前线程.threadLocals = <ThreadLocal,要存储对象>赋值给当前线程的ThreadLocalMap 的属性
//t.threadLocals = new ThreadLocalMap(this, firstValue);
createMap(t, value);
}
2.get():ThreadLocal为当前线程过去对象方法,该方法返回当前线程所对应的线程局部变量。如果不存在则返回一个null。
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals
ThreadLocalMap map = getMap(t);
if (map != null) {
//获取当前threadLocals 的 Entry对象 Entry
//Entry的成员变量为
//Object value;
//Entry的构造方法为
//Entry(ThreadLocal<?> k, Object v) {
// super(k);
// value = v;
//}
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
//e 不为空 获取存放的对象
T result = (T)e.value;
return result;
}
}
//为当先线程初始化一个 threadLocals 并且存放的value 为 null 并返回null
return setInitialValue();
}
三、使用
public class ThreadLocalTest {
public static void main(String[] args) {
ThreadLocal<Person> threadLocal = new ThreadLocal<>();
new Thread(() -> {
threadLocal.set(new Person("李四"));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadLocal.get().name);
}).start();
new Thread(() -> {
threadLocal.set(new Person("张三"));
System.out.println(threadLocal.get().name);
}).start();
}
}
class Person{
String name;
public Person(String name) {
this.name = name;
}
}
注意:spring的显式声明事务用到了ThreadLocal,因为一个事物内的增删改查是基于一个connection的,不可能每一次与数据交互都是一个新的connection,所以一个事物内用ThreadLocal存储了本次事物的connection。
四、ThreadLocal和synchronized对比
ThreadLocal:每个线程一个对象资源。
synchronized:多个线程竞争一个对象资源。
注意:ThreadLocal存储的对象不用时必须用remove()回收掉,否则有内存泄漏风险。