池化线程数据传递: TransmittableThreadLocal源码分析

要解决什么问题

JDKInheritableThreadLocal类可以完成父线程到子线程的值传递。但对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时ThreadLocal值传递到 任务执行时

在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能

关于InheritableThreadLocal

放在哪里

每个线程内部有这么一个属性

		 /*
     * 与此线程有关的InheritableThreadLocal值 
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

本质上是一个弱引用数组

    static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
      
        ...
    }
何时初始化的
父线程第一次调用set
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
子线程初始化的时候

java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean)

		if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
何时赋值的

父线程调用set

子线程初始化

TransmittableThreadLocal

本质

本质上是一个InheritableThreadLocal

	public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> implements TtlCopier<T> 	

存放在线程内部的inheritableThreadLocals,不涉及线程池时可以当成inherateAbleThreadLocal使用

  ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

值保存在哪里

重写get和set

然后重写了get和set方法,这里是比较特别的地方是有一个addThisToHolder(),见名知意。

    /**
     * see {@link InheritableThreadLocal#get()}
     */
    @Override
    public final T get() {
        T value = super.get();
        if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
        return value;
    }
    
     /**
     * see {@link InheritableThreadLocal#set}
     */
    @Override
    public final void set(T value) {
        if (!disableIgnoreNullValueSemantics && null == value) {
            // may set null to remove value
            remove();
        } else {
            super.set(value);
            addThisToHolder();
        }
    }


    private void addThisToHolder() {
        if (!holder.get().containsKey(this)) {
            holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
        }
    }

TransmittableThreadLocal核心属性 #holder

    
		// 1. holder 本质上是一个 InheritableThreadLocal(a *ThreadLocal*).
    // 2. 内部的值的类型是 WeakHashMap<TransmittableThreadLocal<Object>, ?>.
    //    2.1 这个 WeakHashMap 被当成一个 *Set* 使用:
    //        这个WeakHashMap的value永远是null, 不会被使用到.
    //    2.2 WeakHashMap 支持 *null* 作为value.
		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);
                }
            };
holder的作用(存放的是所有和当前线程产生关联的 TransmittableThreadLocal)

当线程首次调用TransmittableThreadLocal<Object>类型变量 A 的get或者set方法,实际上做了两个动作,在现成的

    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

中插入两个值

  • key为TransmittableThreadLocal<Object>的Entry
  • key为InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, null>>的Entry(注意这个entry当Set使用的)

也就是说,任何一个当前线程只要调用任意一个TransmittableThreadLocal类型的变量的get或set方法,那么这个TransmittableThreadLocal变量就会被holder偷偷记录下来。

当我们使用TransmittableThreadLocal时, 对线程意味着什么

线程会会持有所有自己使用过的TransmittableThreadLocal的引用。

线程的inheritableThreadLocals属性会有一个被holder创建的WeakHashMap<TransmittableThreadLocal<Object>, null>,里面记录了所有和当前线程有过关联的TransmittableThreadLocal

值如何传递

  • 修饰RunnableCallable

  • 修饰线程池

我们来写话应该怎么做?

一句话:把提交任务线程的TransmittableThreadLocalcopy一份到当前线程。

盲点:copy之后能够还原线程池线程的值

以TtlRunnable作为示例分析一下
public final class TtlRunnable implements Runnable, TtlWrapper<Runnable>, TtlEnhanced, TtlAttachments 
复制

主要关注一下静态工厂方法

    /**
     * Factory method, wrap input {@link Runnable} to {@link TtlRunnable}.
     *
     * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}.
     * @return Wrapped {@link Runnable}
     * @throws IllegalStateException when input is {@link TtlRunnable} already.
     */
    @Nullable
    public static TtlRunnable get(@Nullable Runnable runnable) {
        return get(runnable, false, false);
    }

		// 调用私有构造函数
    private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
        this.capturedRef = new AtomicReference<Object>(capture());
        this.runnable = runnable;
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
    }

		
		// HashMap<ThreadLocal<Object>, Object> 和 HashMap<TransmittableThreadLocal<Object>, Object>
    private final AtomicReference<Object> capturedRef;

这里涉及到了一个比较重要的变量capturedRef,本质上是一个原子对象引用。

这个引用指向的是一个快照,快照里是当前线程的TransmittableThreadLocal值的副本

 		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获取和当前线程有关联的`TransmittableThreadLocal`,挨个复制,然后返回
      for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
        ttl2Value.put(threadLocal, threadLocal.copyValue());
      }
      return ttl2Value;
    }

到此为止,父线程的值已经全部接收了下来;

运用

被包装的run方法

		@Override
    public void run() {
     		// 获取到副本里的值
        final Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }

      	// 回放提交线程的副本,返回当前线程(线程池线程)的备份
        final Object backup = replay(captured);
        try {
            runnable.run();
        } finally {
          	// 执行完毕,将当前线程(线程池线程)的值恢复回来
            restore(backup);
        }
    }

回放

	@NonNull
  public static Object replay(@NonNull Object captured) {
    final Snapshot capturedSnapshot = (Snapshot) captured;
    return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
  }

@NonNull
private static HashMap<TransmittableThreadLocal<Object>, Object> replayTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
    HashMap<TransmittableThreadLocal<Object>, Object> backup = new HashMap<TransmittableThreadLocal<Object>, Object>();

  	//这个holder.get在当前线程没有使用过TransmittableThreadLocal时为空
    for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
      //能进入这里说明当前线程执行过TransmittableThreadLocal有关操作
        TransmittableThreadLocal<Object> threadLocal = iterator.next();

        // backup
        backup.put(threadLocal, threadLocal.get());

        // clear the TTL values that is not in captured
        // avoid the extra TTL values after replay when run task
        if (!captured.containsKey(threadLocal)) {
          //如果启动线程的TransmittableThreadLocals里没有包含这个额TransmittableThreadLocal
          //把当前线程的有关这个TransmittableThreadLocal的数据清空
          iterator.remove();
          threadLocal.superRemove();
        }
    }

    // set TTL values to captured
    setTtlValuesTo(captured);

    // call beforeExecute callback
    doExecuteCallback(true);

    return backup;
}

private static void setTtlValuesTo(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> ttlValues) {
  //对于启动线程里面的值 挨个加入到当前线程里
    for (Map.Entry<TransmittableThreadLocal<Object>, Object> entry : ttlValues.entrySet()) {
      TransmittableThreadLocal<Object> threadLocal = entry.getKey();
      threadLocal.set(entry.getValue());
    }
}

恢复就不说了

复盘

主要依靠holder记录所有关联的TransmittableThreadLocal

通过包装的ttlRunnable或者ttlCallable,生成任务时产生一个提交线程的快照,

重播并备份,恢复,完事

简单思考

问题一

先set,然后生成ttlRunnable,再set,然后提交 ,我觉得会出问题,快照已经保存过了的(在生成任务的时候保存快照;

这一点就有趣了。但道理上也讲得通;

但是还是建议使用包装线程池的方式。

问题二

主线程启动子线程 子线程再提交一个任务到线程池,holder会不会没有和子线程关联?

holder本质上也是inheritAbleThreadLocal,会被赋值给子线程 这里间接地和holder产生了关联

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值