Threadlocal在Dubbo的妙用

RpcContext

大家知道RpcContext是记录一次调用的上下文信息。源码中的注释如下(RpcContext是一个临时状态持有者。每次发送或接收请求时,RpcContext中的状态都会更改)

/**
 * Thread local context. (API, ThreadLocal, ThreadSafe)
 * Note: RpcContext is a temporary state holder. States in RpcContext changes every time when request is sent or received.(RpcContext是一个临时状态持有者。每次发送或接收请求时,RpcContext中的状态都会更改)
 * For example: A invokes B, then B invokes C. On service B, RpcContext saves invocation info from A to B before B
 * starts invoking C, and saves invocation info from B to C after B invokes C.
 */

是通过Threadlocal实现的,不过这里的Threadlocal不是JDK中的Threadlocal,而是经过优化过,在Dubbo中叫InternalThreadLocal。

/**
 * InternalThreadLocal
 * A special variant of {@link ThreadLocal} that yields higher access performance when accessed from a {@link InternalThread}.
 (当从InternalThread访问时,可产生更高的访问性能。)
 * Internally, a {@link InternalThread} uses a constant index in an array, instead of using hash code and hash table,
 * to look for a variable.  Although seemingly very subtle, it yields slight performance advantage over using a hash
 * table, and it is useful when accessed frequently.
 * This design is learning from {@see io.netty.util.concurrent.FastThreadLocal} which is in Netty.
 */
public class InternalThreadLocal

可以看到设计思想来自netty中的FastThreadLocal。主要是通过空间换时间,在ThreadLocal中如果有hash碰撞,是通过线性搜索解决的,使得获取时不能达到完美的O(1),InternalThreadLocal在数组中使用常量索引,而不是使用哈希码和哈希表来查找变量。尽管看似非常微妙,但与使用hash表相比,它在性能上却有一点优势,并且在经常访问时很有用。

Threadlocal最终还是跟Thread内中的ThreadlocalMap挂钩的,因此这一块的设计在InternalThreadLocal中是相同。

image

在Dubbo业务线程池创建调用时也是使用线程工厂创建InternalThread来支持后续InternalThreadLocal的使用。


image

Threadlocal内部模型

image

这个是Threadlocal中的使用流程。通过hashcode+线性探测法。

InternalThreadlocal内部模型

image

而在InternalThreadLocal的内部模型则如上图所示,通过常量索引来进行优化。在大幅使用上可以进行一部分的性能优化。

源码分析

初始化时进行索引分配

public class InternalThreadLocal<V> {

    private final int index;

    public InternalThreadLocal() {
        //初始化时,获取常量索引
        index = InternalThreadLocalMap.nextVariableIndex();
    }
}

InternalThreadLocalMap中的分配index方法。

public final class InternalThreadLocalMap {
    //发号器
    private static final AtomicInteger NEXT_INDEX = new AtomicInteger();

    public static int nextVariableIndex() {
        int index = NEXT_INDEX.getAndIncrement();
        //说明超过Int的最大值了
        if (index < 0) {
            NEXT_INDEX.decrementAndGet();
            throw new IllegalStateException("Too many thread-local indexed variables");
        }
        return index;
    }
}

InternalThreadLocal中的get方法,结合当前的index直接获取相应的值。

public final V get() {
    //获取当前InternalThread中的map
    InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
    //根据索引直接获取值,O(1)
    Object v = threadLocalMap.indexedVariable(index);
    if (v != InternalThreadLocalMap.UNSET) {
        return (V) v;
    }
    
    return initialize(threadLocalMap);
}

写的有错的地方或者不好的地方,欢迎大家提出意见或者建议!(鞠躬

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。