Dubbo之InternalThreadLocal源码解析

功能概述

  • 对ThreadLocal的封装处理,内部使用的数据结构是数组,而ThreadLocal是使用hashCode来计算处理的,多了一步计算,还得解决hash冲突,所以InternalThreadLocal的访问性能更高
  • InternalThreadLocalMap:内部的线程局部变量的Map【用于存储线程的局部变量值,存储的结构是一个数组,而不是一个Map(快慢获取的元素,本质在于数组结构的不同)】

功能分析

核心类InternalThreadLocal分析

主要成员变量分析

private static final int VARIABLES_TO_REMOVE_INDEX = InternalThreadLocalMap.nextVariableIndex(); //InternalThreadLocal类型的缓存集合在InternalThreadLocalMap对应的下标

private final int index; //普通的缓存对象在InternalThreadLocalMap对应的下标(final修饰的变量为常量,表明一旦赋值后,就不能再改动,所以可以看出是一个对象一个index值(所以同一个InternalThreadLocal对象多次设值时,是会出现覆盖的)

主要成员方法分析

构造方法
public InternalThreadLocal() { //构建对象时,由InternalThreadLocalMap分配下标
    index = InternalThreadLocalMap.nextVariableIndex(); //设置下一个游标值,每使用一个InternalThreadLocal,游标就会+1
}
移除所有缓存值
public static void removeAll() { //移除所有绑定在当前线程的缓存值(包含普通对象和InternalThreadLocal对象)
    InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
    if (threadLocalMap == null) {
        return;
    }

    try {
        Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX); //拿到InternalThreadLocal类型的缓存集合对应的下标
        if (v != null && v != InternalThreadLocalMap.UNSET) {
            Set<InternalThreadLocal<?>> variablesToRemove = (Set<InternalThreadLocal<?>>) v;
            InternalThreadLocal<?>[] variablesToRemoveArray =
                    variablesToRemove.toArray(new InternalThreadLocal[variablesToRemove.size()]); //将集合Set转换为Map
            for (InternalThreadLocal<?> tlv : variablesToRemoveArray) { //将每个InternalThreadLocal做移除操作
                tlv.remove(threadLocalMap);
            }
        }
    } finally {
        InternalThreadLocalMap.remove();
    }
}
添加InternalThreadLocal变量
private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, InternalThreadLocal<?> variable) {//添加InternalThreadLocal变量到缓存集合中(即处理第一个元素)
    Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX);
    Set<InternalThreadLocal<?>> variablesToRemove; //表示包含了多少个InternalThreadLocal实例
    if (v == InternalThreadLocalMap.UNSET || v == null) {
        variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<InternalThreadLocal<?>, Boolean>()); //将Map值转换为Set
        threadLocalMap.setIndexedVariable(VARIABLES_TO_REMOVE_INDEX, variablesToRemove);
    } else {
        variablesToRemove = (Set<InternalThreadLocal<?>>) v; //进行类型强转
    }

    variablesToRemove.add(variable); //InternalThreadLocalMap中的第一个元素是集合类型,如indexedVariables[0]为Collections$SetFromMap@1751
}
获取当前线程维护的值
public final V get() { //从当前线程中获取当前维护的值(若没有查找到值,会进行初始化)
    InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); //与set()获取InternalThreadLocalMap方式一致
    Object v = threadLocalMap.indexedVariable(index); //获取当前对象对应index对应的值
    if (v != InternalThreadLocalMap.UNSET) { //若值不为UNSET,则直接返回
        return (V) v;
    }

    return initialize(threadLocalMap); //若不存在值,则进行初始化操作
}
初始化操作
private V initialize(InternalThreadLocalMap threadLocalMap) { //做初始化,并返回初始化后的值
    V v = null;
    try {
        v = initialValue(); //调用子类重写的方法,若子类没有重写,则值为null
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

    threadLocalMap.setIndexedVariable(index, v); //此处设置的逻辑,和set()的内部逻辑一致
    addToVariablesToRemove(threadLocalMap, this);
    return v;
}
设置值到当前线程
public final void set(V value) {
    if (value == null || value == InternalThreadLocalMap.UNSET) {
        remove(); //设置的值为空时,做移除处理(与调用remove()方法是等价的)
    } else {
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        if (threadLocalMap.setIndexedVariable(index, value)) { //将值设置到InternalThreadLocalMap维护的数组中
            addToVariablesToRemove(threadLocalMap, this); //将当前的InternalThreadLocal对象设置到缓存的InternalThreadLocal集合中
        }
    }
}
移除单个缓存值
public final void remove(InternalThreadLocalMap threadLocalMap) { //从InternalThreadLocalMap中移除指定下标index对应的值(此处语义上不太好理解,就是没有通过方法参数传递index,而是通过操作成员变量的方式)
    if (threadLocalMap == null) {
        return;
    }

    Object v = threadLocalMap.removeIndexedVariable(index); // 1)移除指定下标的普通对象的缓存值,并返回移除前的值
    removeFromVariablesToRemove(threadLocalMap, this); // 2)将当前InternalThreadLocal对象从InternalThreadLocal类型的缓存集合中移除

    if (v != InternalThreadLocalMap.UNSET) { //当前InternalThreadLocal有设置过值,则对应回调子类方法

        try {
            onRemoval((V) v); // 3)看子类的具体移除实现(回调子类的方法)
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

核心类InternalThreadLocalMap分析

主要成员变量分析

private Object[] indexedVariables; //缓存对象对应的数组(不是static变量,非共享,每个对象各自维护,是线程安全的)

private static ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>(); //原生的ThreadLocal,每个线程维护各自的线程变量(原生的ThreadLocal使用get()获取值时,会通过计算hashCode进行查找处理)

private static final AtomicInteger NEXT_INDEX = new AtomicInteger(); //记录数组可设值的下标(下一个设置的值,对应的下标,static变量,属于公共资源,初始值为0)

public static final Object UNSET = new Object(); //当未设置值时,给出的默认值(用于填充使用)

主要成员方法分析

获取InternalThreadLocalMap
public static InternalThreadLocalMap get() { //获取InternalThreadLocalMap,返回的值若为空,会初始化对象返回
    Thread thread = Thread.currentThread();
    if (thread instanceof InternalThread) {
        return fastGet((InternalThread) thread); //比较快的获取值
    }
    return slowGet(); //比较慢的获取值
}
设置线程局部变量的值
public boolean setIndexedVariable(int index, Object value) { //设置线程局部变量的值(设置成功返回true)
    Object[] lookup = indexedVariables; //引用赋值,lookup数组改变,indexedVariables数组也对应改变
    if (index < lookup.length) {
        Object oldValue = lookup[index];
        lookup[index] = value;
        return oldValue == UNSET; //@csy 此处是何意?解:若老的值为UNSET,说明值已经从UNSET -> value改变了
    } else {
        expandIndexedVariableTableAndSet(index, value); //扩容处理
        return true;
    }
}
快速获取InternalThreadLocalMap
private static InternalThreadLocalMap fastGet(InternalThread thread) { //比较快的获取InternalThreadLocalMap
    InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); //从InternalThread直接获取
    if (threadLocalMap == null) {
        thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
    }
    return threadLocalMap;
}
较慢获取InternalThreadLocalMap
private static InternalThreadLocalMap slowGet() { //比较慢的获取InternalThreadLocalMap
    ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = InternalThreadLocalMap.slowThreadLocalMap;
    InternalThreadLocalMap ret = slowThreadLocalMap.get(); //使用ThreadLocal获取,内部是通过hashCode去获取值的
    if (ret == null) {
        ret = new InternalThreadLocalMap(); //初始化InternalThreadLocalMap
        slowThreadLocalMap.set(ret); //将值设置set到ThreadLocal中(get时可取到set的值)
    }
    return ret;
}
扩容并设置线程变量的值
private void expandIndexedVariableTableAndSet(int index, Object value) { //扩容并设置线程变量的值
    Object[] oldArray = indexedVariables;
    final int oldCapacity = oldArray.length;
    int newCapacity = index;
    newCapacity |= newCapacity >>> 1; //无符号右移
    newCapacity |= newCapacity >>> 2;
    newCapacity |= newCapacity >>> 4;
    newCapacity |= newCapacity >>> 8;
    newCapacity |= newCapacity >>> 16;
    newCapacity++;

    Object[] newArray = Arrays.copyOf(oldArray, newCapacity); //使用数组拷贝
    Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
    newArray[index] = value;
    indexedVariables = newArray;
}

问题点答疑

  • InternalThread.get值时,是怎么区分当前线程的?一个线程能存储多个变量吗?

    • 解答:通过Thread.currentThread(); if (thread instanceof InternalThread) 来判断 一个线程可以创建多个InternalThreadLocal对象,每个InternalThreadLocal维护一个对象值,所以一个线程是存储多个变量的
  • InternalThreadLocalMap#getIfSet待了解?

    • 解答:从InternalThread中或InternalThreadLocalMap维护的ThreadLocal获取到InternalThreadLocalMap值
  • InternalThreadLocalMap功能用途是怎样的?怎么使用的?

    • 解答:用来存储线程局部变量的数据结构,包含的成员变量有缓存对象对应的数组以及下标
  • InternalThreadLocalMap#fastGet与slowGet有什么差异?

    • 解答:fastGet()是从InternalThread获取的,获取会快些,而slowGet()是从ThreadLocal获取的,获取会慢些
  • InternalThreadLocal#VARIABLES_TO_REMOVE_INDEX变量的用途是什么?(OK)

    • 解答:InternalThreadLocalMap#indexedVariables维护的数组中既有InternalThreadLocal对应的集合,也有对应的缓存对象。
  • InternalThreadLocal与InternalThread、InternalThreadLocalMap三者之间是怎么关联的?(OK)

    • 解答:InternalLocalThreadMap:负责值的存储以及下标的产生InternalThreadLocal:按对象维度隔离数据,每个对象的值存储在InternalThreadLocalMap指定下标的元素中InternalThread: 按线程维度隔离数据,每个线程各自的私有变量都存在各自的InternalThradLocalMap中

归纳总结

  • 在Java中,ThreadLocal是实现线程安全的一种手段,它的作用是对于同一个ThreadLocal变量,在每一个线程中都有一个副本,当修改任何一个线程的变量时,不会影响到其他线程。它通过在每一个Thread中存储一个类似于map的结构,以ThreadLocal变量为key,变量值为value。Dubbo在RPC调用的上下文中,需要借助ThreadLocal保存上下文。通过ThreadLocal,可用于传递参数InternalThreadLocal 是 ThreadLocal 的增强版,所以他们的用途都是一样的,一言蔽之就是:传递信息。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值