TransmittableThreadLocal实现原理

TransmittableThreadLocal实现父线程值传递变更值给线程池子线程

InheritableThreadLocal只会在线程池中的线程初次创建的时候,从父线程拷贝属性,而父线程再次修改这个属性时,线程池中的这个线程是无法再次感知到的。

我们在日常的开发中,很少主动new线程,都是使用线程池,而线程池中,不会频繁的创建线程,更多的场景是线程创建一次,重复使用。

如果线程中的属性在上次使用后被修改,下次使用后,子线程中保存的属性值依然是首次使用时的值

TransmittableThreadLocal实现父线程值传递变更值给线程池子线程

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {
 
    public static TransmittableThreadLocal<String> param = new TransmittableThreadLocal<>();
 
    public static void main(String[] args) {
        param.set("main");
 
        ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));
 
        pool.submit(() -> {
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }
 
}



----------------------
main
main2
main2

可以看到,使用TransmittableThreadLocal并配合TtlExecutors.getTtlExecutorService()可以让子线程感知到父线程对共享变量的实时变化。

子线程对父线程值的修改只会影响当前运行时子线程,不影响父线程和其他子线程,也不影响下次运行

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * @author : mazhen
 * Date    : 2021/11/12
 * Time    : 4:11 下午
 * ---------------------------------------
 * Desc    :
 */
public class ITL {
 
    public static TransmittableThreadLocal<String> param = new TransmittableThreadLocal<>();
 
    public static void main(String[] args) {
        param.set("main");
 
        ExecutorService pool = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));
 
        pool.submit(() -> {
            System.out.println(param.get());
        });
        param.set("main2");
        pool.submit(() -> {
            param.set("child value only use in this");
            System.out.println(param.get());
        });
        pool.submit(() -> {
            System.out.println(param.get());
        });
        pool.shutdown();
    }
 
}


--------------
main
child value only use in this
main2

TransmittableThreadLocal实现原理

与ThreadLocal保存在Thread中不同,holder就保存在TransmittableThreadLocal中:

//holder就保存在TransmittableThreadLocal
private static InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>> holder = new InheritableThreadLocal<Map<TransmittableThreadLocal<?>, ?>>() {
        protected Map<TransmittableThreadLocal<?>, ?> initialValue() {
            return new WeakHashMap();
        }
 
        protected Map<TransmittableThreadLocal<?>, ?> childValue(Map<TransmittableThreadLocal<?>, ?> parentValue) {
            return new WeakHashMap(parentValue);
        }
    };



//看设置值的特点
public final void set(T value) {
     // InheritableThreadLocal.set(),这里保留了父类的特性,即会把值保存到ThreadLocal
     super.set(value);
     if (null == value) {
         this.removeValue();
     } else {
         this.addValue();
     }
 
}
 
 
 
private void addValue() {
    if (!((Map)holder.get()).containsKey(this)) {
       ((Map)holder.get()).put(this, (Object)null);
    }
 
}

set时依然会像thread本身特点一样设置ThreadLocal,但是更重要的是TTL自己也创建了一个holder去保存值,而holder就保存在TransmittableThreadLocal中

TtlExecutors对普通的ExecutorService进行了封装

//封装ExecutorService
public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
        return (ExecutorService)(!TtlAgent.isTtlAgentLoaded() && executorService != null && !(executorService instanceof TtlEnhanced) ? new ExecutorServiceTtlWrapper(executorService) : executorService);
    }




///ExecutorServiceTtlWrapper的实现
public <T> Future<T> submit(@Nonnull Runnable task, T result) {
    return this.executorService.submit(TtlRunnable.get(task), result);
}
 
//核心就是TtlRunnable
public static TtlRunnable get(@Nullable Runnable runnable) {
    return get(runnable, false, false);
}
 
 
public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
    if (null == runnable) {
         return null;
    } else if (runnable instanceof TtlEnhanced) {
         if (idempotent) {
              return (TtlRunnable)runnable;
         } else {
             throw new IllegalStateException("Already TtlRunnable!");
         }
    } else {
         return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
    }
}




//TtlRunnable实现了runable,可以看到运行业务前后它会做子线程前后的现场备份和恢复工作
@Override
public void run() {
        /**
         * capturedRef是主线程传递下来的ThreadLocal的值。
         */
        Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
        throw new IllegalStateException("TTL value reference is released after run!");
        }
        /**
         * 1.  backup(备份)是子线程已经存在的ThreadLocal变量;
         * 2. 将captured的ThreadLocal值在子线程中set进去;
         */
        Object backup = replay(captured);
        try {
        /**
         * 待执行的线程方法;
         */
        runnable.run();
        } finally {
        /**
         *  在子线程任务中,ThreadLocal可能发生变化,该步骤的目的是
         *  回滚{@code runnable.run()}进入前的ThreadLocal的线程
         */
        restore(backup);
        }
        }
/**
 * 将快照重做到执行线程
 * @param captured 快照
 */
public static Object replay(Object captured) {
// 获取父线程ThreadLocal快照
final Snapshot capturedSnapshot = (Snapshot) captured;
        return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
        }

/*****************************************************
 * 重放TransmittableThreadLocal,并保存执行线程的原值
 ****************************************************/
private static WeakHashMap<TransmittableThreadLocalCode<Object>, Object> replayTtlValues(WeakHashMap<TransmittableThreadLocalCode<Object>, Object> captured) {
        WeakHashMap<TransmittableThreadLocalCode<Object>, Object> backup = new WeakHashMap<TransmittableThreadLocalCode<Object>, Object>();

        for (final Iterator<TransmittableThreadLocalCode<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
        TransmittableThreadLocalCode<Object> threadLocal = iterator.next();

        // backup
        // 遍历 holder,从 父线程继承过来的,或者之前注册进来的
        backup.put(threadLocal, threadLocal.get());

        // clear the TTL values that is not in captured
        // avoid the extra TTL values after replay when run task
        // 清除本次没有传递过来的 ThreadLocal,和对应值
        //  -- 第一点:可能会有因为 InheritableThreadLocal 而传递并保留的值
        //  -- 第二点:保证主线程set过的ThreadLocal不被传递过来。明确其传递是由业务代码控制,就是明确 set 过值的
        if (!captured.containsKey(threadLocal)) {
        iterator.remove();
        threadLocal.superRemove();
        }
        }

        // set TTL values to captured
        // 将 map 中的值,设置到快照
        // 内部调用了 beforeExecute 和 afterExecute 方法。默认不做任何处理
        setTtlValuesTo(captured);

        // call beforeExecute callback
        // TransmittableThreadLocal 的回调方法,在任务执行前执行
        doExecuteCallback(true);

        return backup;
        }

private static WeakHashMap<ThreadLocal<Object>, Object> replayThreadLocalValues( WeakHashMap<ThreadLocal<Object>, Object> captured) {
final WeakHashMap<ThreadLocal<Object>, Object> backup = new WeakHashMap<ThreadLocal<Object>, Object>();

        for (Map.Entry<ThreadLocal<Object>, Object> entry : captured.entrySet()) {
final ThreadLocal<Object> threadLocal = entry.getKey();
        backup.put(threadLocal, threadLocal.get());

final Object value = entry.getValue();
        // 如果值是标记已删除,则清除
        if (value == threadLocalClearMark) threadLocal.remove();
        else threadLocal.set(value);
        }

        return backup;
        }
/*********************************************
 * 恢复备份的原快照
 *********************************************/
public static void restore( Object backup) {
// 将之前保存的TTL和threadLocal原来的数据覆盖回去
final Snapshot backupSnapshot = (Snapshot) backup;
        restoreTtlValues(backupSnapshot.ttl2Value);
        restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
        }

private static void restoreTtlValues( WeakHashMap<TransmittableThreadLocalCode<Object>, Object> backup) {
        // call afterExecute callback
        // 调用执行完后回调接口
        doExecuteCallback(false);

        // 移除子线程新增的TTL
        for (final Iterator<TransmittableThreadLocalCode<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
        TransmittableThreadLocalCode<Object> threadLocal = iterator.next();
        // 恢复快照时,清除本次传递注册进来,但是原先不存在的 TransmittableThreadLocal
        // 移除掉所有不在备份里面的TTL数据,应该是为了避免内存泄漏吧
        // clear the TTL values that is not in backup
        // avoid the extra TTL values after restore
        if (!backup.containsKey(threadLocal)) {
        iterator.remove();
        threadLocal.superRemove();
        }
        }

        // 重置为原来的数据(就是恢复回备份前的值)
        // restore TTL values
        setTtlValuesTo(backup);
        }

private static void setTtlValuesTo( WeakHashMap<TransmittableThreadLocalCode<Object>, Object> ttlValues) {
        for (Map.Entry<TransmittableThreadLocalCode<Object>, Object> entry : ttlValues.entrySet()) {
        TransmittableThreadLocalCode<Object> threadLocal = entry.getKey();
        // set 的同时,也就将 TransmittableThreadLocal 注册到当前线程的注册表了
        threadLocal.set(entry.getValue());
        }
        }

步骤:
1装饰Runnable,将主线程的TTL传入到TtlRunnable的构造方法中
2将子线程的TTL的值进行备份,将主线程的TTL设置到子线程中(value是对象引用,可能存在线程安全问题);
3执行子线程逻辑
4删除子线程新增的TTL,将备份还原重新设置到子线程的TTL中

其他注意点

TTL为什么不直接继承ThreadLocal?
因为有些业务需要用到ITL特性,如果直接继承ThreadLocal,就会丢失ITL的父拷贝到子线程数据的特性(子线程创建时拷贝)
为什么需要在run执行完之后调用restore()?
restore里面会主动调用remove()回收,避免内存泄露(会删除子线程新增的TTL)
不调用restore()的话,就会覆盖之前backup备份部分子线程的数据,这样可能在业务上有隐患
TTL存在线程安全问题?
存在的,因为默认都是引用类型拷贝,如果子线程修改了数据,主线程是可以感知到的
TTL是否存在内存泄露问题?
TTL维护的holder本身是一个static来的,使用的时候会调用restore(),然后里面显式调用remove()清楚子线程新增TTL,所以正确使用下是没有内存泄露问题的

参考原文链接:https://blog.csdn.net/qq_28666081/article/details/119834058

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值