ThreadLocal 源码解析
ThreadLocal 源码解析
ThreadLocal 是一个支持变量由线程独有且相互隔离互不影响的类。
举个例子
A,B,C三个线程同时操作一个类Demo的实例对象d,类Demo中有一个ThreadLocal的变量d且初始值为0,A,B,C分别对这个变量d进行循环加1,循环10次,最后输出,那么A,B,C执行结束后这个值在每个线程的打印结果都是10。
public class Demo implements Runnable{ public ThreadLocal<Integer> local = ThreadLocal.withInitial(()->0);//初始化 @Override public void run() { for (int i = 0; i < 10; i++) { Integer val = local.get(); local.set(val + 1); } System.out.println(Thread.currentThread().getName()+" "+local.get()); } }
运行测试一下
TDemo tDemo = new TDemo(); new Thread(tDemo,"t1").start(); new Thread(tDemo,"t2").start(); new Thread(tDemo,"t3").start(); System.out.println(Thread.currentThread().getName()+" "+tDemo.local.get());
输出
main 0 t1 10 t3 10 t2 10
总结:ThreadLocal是每个线程独享的变量
如果是只需知道ThreadLocal在什么场景下使用,以及如何使用,了解到这里已经足够了,如果还想了解原理,请往下看源码解析,我以例子为样本中t1线程来说明
源码解析
初始化
// Supplier是一个lambda表达式接口,我传入的是()->10
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
/**
* 你可能疑惑为何withInitial方法的返回类型是ThreadLocal,返回SuppliedThreadLocal对象却不报错,
* 因为SuppliedThreadLocal是ThreadLocal的一个子类,SuppliedThreadLocal也是ThreadLocal里的一
* 个内部类。
*/
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;//存储lambda表达式
//SuppliedThreadLocal的构造器,这个构造器就做了一件事:存储lambda表达式
SuppliedThreadLocal(Supplier<? extends T> supplier) {
// requireNonNull就检查一下传入的supplier是否为空,为空抛异常,不为空返回原参数;supplier就是()->{return 10;}
this.supplier = Objects.requireNonNull(supplier);
}
...
}
java.util.Objects#requireNonNull(T)
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
读完上述源码了解到:执行ThreadLocal<Integer> local = ThreadLocal.withInitial(()->0);
初始化,仅为ThreadLocal的子类SuppliedThreadLocal对象中的supplier属性赋值了lambda表达式。
接着往下走。
get获取值
public T get() {
Thread t = Thread.currentThread();//用t标记当前正在执行此代码的线程(t1)
ThreadLocalMap map = getMap(t);//获取当前线程t的ThreadLocalMap
if (map != null) {
...
}
...
}
getMap
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
看到这里,getMap拿线程t中的threadLocals(ThreadLocalMap
类型),说明threadLocals是Thread类中的一个属性
public class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
获取到的threadLocals为null,那么map就为null,好!接着往下走
public T get() {
Thread t = Thread.currentThread();//用局部变量t标记当前正在执行此代码的线程(t1)
ThreadLocalMap map = getMap(t);//获取当前线程t的ThreadLocalMap
// 上一步知晓了map=null,那么这个if判断一定不会进,请直接看setInitialValue方法
if (map != null) {
...
}
return setInitialValue();
}
setInitialValue
private T setInitialValue() {
T value = initialValue();//value等于执行lambda表达式返回的结果
Thread t = Thread.currentThread();//用局部变量t标记当前正在执行此代码的线程
ThreadLocalMap map = getMap(t);//获取当前线程(t1)的ThreadLocalMap
// get方法中就知晓了map=null,这个if判断一定不进,请直接看else中的createMap方法
if (map != null)
map.set(this, value);
else
createMap(t, value);//创建ThreadLocalMap
return value;//返回value,0
}
initialValue
@Override protected T initialValue() { return supplier.get();//执行lambda表达式,也就是()->0,返回结果0 }
createMap
/**
* 创建ThreadLocalMap
*
* @param t t1线程
* @param firstValue 在setInitialValue方法中可知value=null,所以firstValue=0
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap 构造方法 :start: :start:
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;//用于存放ThreadLocal的数据,如例子中的local变量
/**
* @param firstKey createMap方法中传入的this,ThreadLocal对象
* @param firstValue createMap方法中传入的0
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];//初始化一个Entry数组,长度默认16
// 计算下标,对象放在table[]的哪个位置
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// new一个Entry对象,firstValue为0,并把此对象存入table中
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
Entry构造器
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
父类 WeakReference 构造器
public class WeakReference<T> extends Reference<T> { public WeakReference(T referent) { super(referent); } ... }
父类的父类的构造器
Reference(T referent) { this(referent, null); } Reference(T referent, ReferenceQueue<? super T> queue) { this.referent = referent; this.queue = (queue == null) ? ReferenceQueue.NULL : queue; }
从此处看出
Entry
继承了WeakReference
,是一个弱引用类,什么是弱引用?WeakReference不会影响被引用对象的垃圾回收(GC)行为。当只有WeakReference指向某个对象,而没有其他强引用指向该对象时,如果GC运行,那么这个对象就可能被回收
private int threshold;
// ThreadLocalMap方法中传入的len=16
private void setThreshold(int len) {
threshold = len * 2 / 3; //threshold=10
}
set设置值
public void set(T value) {
Thread t = Thread.currentThread();
//获取线程t1的ThreadLocalMap,显然我们刚刚set了一个0,所以map不可能为null
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set 方法
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);//计算下标
//遍历table
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;
}
}
//重新生成一个Entry对象并放入table中
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
只是撸了一遍源码,还没有梳理总结,有时间补上
本文来自博客园,作者:勤匠,转载请注明原文链接:https://www.cnblogs.com/JarryShu/articles/18218678
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现