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();
}

只是撸了一遍源码,还没有梳理总结,有时间补上

posted @   勤匠  阅读(13)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示