ThreadLocal学习

本文深入解析了ThreadLocal的工作原理及其内部实现机制。介绍了ThreadLocal如何为每个线程提供独立的变量副本,避免线程间的数据冲突。通过源代码分析展示了其如何存储和获取线程局部变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是ThreadLocal呢,它到底解决了一个什么问题?
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
在多线程的编程中我们主要关注的是线程的同步,但是我们好像忽略了在有些情况下,我们并不需要同步,需要的是隔离。

ThreadLocal 提供了一个Map 键是当前线程的Thread 对象,值是传入的对象。这样在线程销毁的时候就会销毁对象,不会造成内存的泄漏。
————————让我们结合源代码如何实现————————————
为一个线程保存一个对象的方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

在这里我们可以看出保存的地方是一个java.lang.ThreadLocal.ThreadLocalMap 而键是但前java.lang.ThreadLocal<T> 对象,那为什么是java.lang.ThreadLocal<T> 对象,而不是java.lang.Thread 对象?假如我们用java.lang.Thread 对象当作键,那么当销毁的时候就不能及时释放资源,因为还有对这个线程的引用。

接下来进入方法 java.lang.ThreadLocal.getMap(Thread) 看下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
很简单就是普通的getter ,但是t.threadLocals这个对象是什么,接着来看:
ThreadLocal.ThreadLocalMap threadLocals = null;
原来它是java.lang.Thread 中一个静态的内置类java.lang.ThreadLocal.ThreadLocalMap<T>
这就是最终存储的地方,这个类是定制的只适合存放thread local 的值。
这是存入的方法:
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal key, Object value) {

// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.

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)]) {
ThreadLocal k = e.get();

if (k == key) {
e.value = value;
return;
}

if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}

tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

可以看出它是用一个数组来存放java.lang.ThreadLocal.ThreadLocalMap.Entry 的对象,并且如果已经键存在会更新原来的值。

接下来看一下创建存储的方法:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这个方法创建了一个java.lang.ThreadLocal.ThreadLocalMap 的对象,并把这个对象赋值给当前线程的threadLocals 变量,从而达到每个线程拥有一个自己的备份的目的。
再来看一下如何取放进去的值:
 public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}


很符合我们常用的方法,getter方法,只不过不用输入键的值,而是自动用当前线程当作键来查找对应的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值