Java并发编程之ThreadLocal(一)
简介
当多线程访问共享变量时,往往会发生并发问题,导致获取的数据不准确、不一致,解决这个问题的一种方法就是进行加锁,同时只让一个线程访问共享变量,但是加锁就意味着有额外的资源消耗。而ThreadLocal
是另一种可以解决此问题的方案,其基本原理是每个线程创建的变量都保存在线程本地,多线程之间互不影响,这样就能避免访问共享变量出现的并发问题了。除此之外,ThreadLocal
也解决了一个线程在多个方法中获取同一变量的问题。
基本使用
@Slf4j
public class Test6 {
// 创建ThreadLocal对象
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
threadLocal.set("t1存的数据"); // 存储数据
log.info(threadLocal.get()); // 获取数据
}, "t1").start();
new Thread(() -> {
threadLocal.set("t2存的数据"); // 存储数据
log.info(threadLocal.get()); // 获取数据
}, "t2").start();
}
}
首先创建了一个ThreadLocal
对象并指定存储的数据类型为String
,然后创建了两个线程t1
、t2
,分别存储数据到threadLocal
中,然后再从threadLocal
中取出数据并打印出来,根据输出结果可以看到t1
线程获取到的数据是"t1
存的数据",t2
线程获取到的是"t2
存的线程",实现了线程间的数据隔离。
类结构
Entry
上图是通过ThreadLocal
存储数据的类结构图,最内部是通过Entry
对象以K-V
形式来存储数据,其中Key
是ThreadLocal
对象,value
就是要存储的数据。Entry
类源码如下:
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// Entry的父类WeakReference
public WeakReference(T referent) {
super(referent);
}
// WeakReference的父类Reference
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent; // ThreadLocal存储到了referent属性
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
可以看到Entry
的属性value
就是我们要存的数据,而ThreadLocal
类型的key
最终存到了祖父类Reference
的属性referent
中。
ThreadLocalMap
// 存储数据的数组
private Entry[] table;
// 构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
上面是ThreadLocalMap
源码中的一部分,其中table
是一个Entry
数组,可以用来存储多组数据。构造方法中会对table
进行初始化并执行存储数据操作,后面会介绍操作流程,这里只要了解其结构即可。
Thread
ThreadLocal.ThreadLocalMap threadLocals = null;
Thread
类中有一个ThreadLocalMap
类型的属性threadLocals
,所以通过ThreadLocal
存储的数据都是存在每个线程内部,所以多线程之间做到了数据隔离。
综上,
Thread
类维护了一个ThreadLocalMap
,而ThreadLocalMap
中维护了一个Entry
数组用来存储多个ThreadLocal
的数据,Entry
中存储的是一个ThreadLocal
的数据。
转载自:https://juejin.cn/post/7231932399100706872