TransmittableThreadLocal的原理分析

一、简介

  TransmittableThreadLocal是由阿里开发的一个线程变量传递工具包,解决了InheritableThreadLocal只能再new Thread的时候传递本地变量,无法应用到线程池的问题。可以应用来作链路追踪,传递变量等用途,下面我们来了解一下原理。

二、InheritableThreadLocal

    public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    protected T childValue(T parentValue) {
        return parentValue;
    }

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    // set的时候调用的
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

InheritableThreadLocal重写了父类的三个方法,其中createMap最为关键,他是我们调用ThreadLocal的Tset方法时,调用的。这里是new了一个ThreadLocalMap赋值给了Thread的inheritableThreadLocals变量,那么我就来看一下Thread的属性及方法。

class Thread implements Runnable {

    // 父线程使用
    ThreadLocal.ThreadLocalMap threadLocals = null;

    // 子线程使用
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    
    // 构造器 创建线程
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

}

当我们使用ThreadLocal的时候,是赋值给threadLocals属性,使用InheritableThreadLocal就是把值又赋给了线程的inheritableThreadLocals属性,那么,可以猜测就是在我们new Thread()的时候触发了线程的值传递,下面通过源码验证猜想

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        ···
        
        // 获取父线程
        Thread parent = currentThread();
        
        ···
        
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            // 当inheritThreadLocals为true,并且父线程的inheritableThreadLocals不为null
            // 将父线程的inheritableThreadLocals赋值给子线程的inheritableThreadLocals
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        ···
    }

三、TransmittableThreadLocal

  TransmittableThreadLocal继承自InheritableThreadLocal,因此它可以在创建线程的时候将值传递给子线程,那么怎么确保使用线程池的时候也有效呢?我们来看一下源码

1、构造方法

    // 构造器
    public TransmittableThreadLocal() {
        this(false);
    }
    public TransmittableThreadLocal(boolean disableIgnoreNullValueSemantics) {
        // 是否忽略null值set,默认false
        this.disableIgnoreNullValueSemantics = disableIgnoreNullValueSemantics;
    }

2、set方法

    public final void set(T value) {
        if (!disableIgnoreNullValueSemantics && null == value) {
            // 不忽略null写入,则移除本地线程变量
            remove();
        } else {
            // 调用父类InheritableThreadLocal的set方法
            super.set(value);
            // 将自己添加到静态线程变量holder中
            addThisToHolder();
        }
    }

先看addThisToHolder方法

    private void addThisToHolder() {
        // 判断holder是否存在此TransmittableThreadLocal对象
        if (!holder.get().containsKey(this)) {
            // 不存则添加进holder
            holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
        }
    }

属性holder又是什么呢?

    private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
            new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
                @Override
                protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
                    return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();
                }

                @Override
                protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
                    return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(parentValue);
                }
            };

1、final static修饰的变量,只会存在一份

2、使用了WeakHashMap,弱引用,方便垃圾回收

3、key就是TransmittableThreadLocal对象

remove方法

    public final void remove() {
        // 从holder中移除
        removeThisFromHolder();
        // 调用父类的移除方法,移除值
        super.remove();
    }

3、get方法

    public final T get() {
        // 调用父类的get
        T value = super.get();
        // 如果允许忽略null,或者value不为null,再次添加到holder
        if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
        return value;
    }

4、当我们使用线程池时,需要使用TtlRunnable.get(runnable)对runnable进行包装,或者使用TtlExecutors.getTtlExecutor(executor)对执行器进行包装,才能使线程池的变量传递起效果,那么我们就接着看一下源码的执行流程

TtlExecutors.getTtlExecutor(executor)

    public static Executor getTtlExecutor(@Nullable Executor executor) {
        if (TtlAgent.isTtlAgentLoaded() || null == executor || executor instanceof TtlEnhanced) {
            return executor;
        }
        // 包装执行器
        return new ExecutorTtlWrapper(executor, true);
    } 
    ExecutorTtlWrapper(@NonNull Executor executor, boolean idempotent) {
        this.executor = executor;
        this.idempotent = idempotent;
    }
    public void execute(@NonNull Runnable command) {
        // 实际上也是通过TtlRunnable对原runnable进行包装
        executor.execute(TtlRunnable.get(command, false, idempotent));
    }

可以看到,两种方式原理一样,我们直接看TtlRunnable.get()

    public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
        if (null == runnable) return null;

        if (runnable instanceof TtlEnhanced) {
            if (idempotent) return (TtlRunnable) runnable;
            else throw new IllegalStateException("Already TtlRunnable!");
        }
        // 返回TtlRunnable
        return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
    }

构建TtlRunnable

    private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
        // 原子引用
        this.capturedRef = new AtomicReference<Object>(capture());
        this.runnable = runnable;
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
    }

capture捕获父线程的ttl

        // 存放父线程的值
        public static Object capture() {
            return new Snapshot(captureTtlValues(), captureThreadLocalValues());
        }
        
        private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {
            HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();
            // 遍历了所有holder
            for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
                // copyValue实际上调用了TransmittableThreadLocal的get方法获取线程存储的变量值
                ttl2Value.put(threadLocal, threadLocal.copyValue());
            }
            return ttl2Value;
        }
    
        private static HashMap<ThreadLocal<Object>, Object> captureThreadLocalValues() {
            final HashMap<ThreadLocal<Object>, Object> threadLocal2Value = new HashMap<ThreadLocal<Object>, Object>();
            // 
            for (Map.Entry<ThreadLocal<Object>, TtlCopier<Object>> entry : threadLocalHolder.entrySet()) {
                final ThreadLocal<Object> threadLocal = entry.getKey();
                final TtlCopier<Object> copier = entry.getValue();
                //
                threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
            }
            return threadLocal2Value;
        }

再看TtlRunnable的run方法

    public void run() {
        // 获取Snapshot对象,里面存储了父线程的值
        final Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }
        // 传入capture方法捕获的ttl,然后在子线程重放,也就是调用ttl的set方法,
        // 这样就会把值设置到当前的线程中去,最后会把子线程之前存在的ttl返回
        final Object backup = replay(captured);
        try {
            // 调用原runnable的run
            runnable.run();
        } finally {
            // 
            restore(backup);
        }
    }

 

posted @ 2022-09-13 10:41  上官兰夏  阅读(5529)  评论(0)    收藏  举报