【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

📚欢迎订阅专栏,专栏名《在2B工作中寻求并发是否搞错了什么》
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。

建议先了解ThreadLocal。

【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读

【Java并发】【ThreadLocal】适合初学体质的ThreadLocal

ThreadLocal存在的问题

众所周知,ThreadLocal并没有解决父子间线程传递的问题,比如下面的代码。

java 复制代码
public static void main(String[] args) throws InterruptedException {
    ThreadLocal<String> threadLocal = new ThreadLocal<>();
    // threadLocal set值
    threadLocal.set("main thread info");
    System.out.println("main thread get: " + threadLocal.get());
    // 启动子线程
    new Thread(() -> System.out.println("sub thread get: " + threadLocal.get())).start();
    threadLocal.remove();
}

输出结果

shell 复制代码
main thread get: main thread info
sub thread get: null

InheritableThreadLocal实现父子线程传递

ThreadLocal换为InheritableThreadLocal:

java 复制代码
public static void main(String[] args) throws InterruptedException {
    InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    // threadLocal set值
    threadLocal.set("main thread info");
    System.out.println("main thread get: " + threadLocal.get());
    // 启动子线程
    new Thread(() -> System.out.println("sub thread get: " + threadLocal.get())).start();
    threadLocal.remove();
}

输出结果

shell 复制代码
main thread get: main thread info
sub thread get: main thread info

🤔,那我们什么我换成InheritableThreadLocal就可以了呢?

省流版 :原理就是,主线程创建子线程的时候,子线程会拷贝1份主线程的ThreadLocalMap

当我们InheritableThreadLocalset值的时候:

初始化当前线程的ThreadLocalMapInheritableThreadLocal会,new一个ThreadLocalMap,赋值给当前线程的inheritableThreadLocals字段。(而ThreadLocal的逻辑是,new一个ThreadLocalMap,赋值给当前线程的threadLocals字段)

java 复制代码
// ThreadLocal#set
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        // 👇初始化ThreadLocalMap,createMap调用子类InheritableThreadLocal类的实现
        createMap(t, value);	
    }
}

// InheritableThreadLocal#createMap
void createMap(Thread t, T firstValue) {
    // 当前线程的inheritableThreadLocals字段
    t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

// ThreadLocal#createMap
void createMap(Thread t, T firstValue) {
    // 当前线程的threadLocals字段
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

聪明的你一定会问了,主播,主播,赋值给inheritableThreadLocals有什么用呢?

那这可是大有说法的,让我们从Thread类开始说起,当我们在主线程,new Thread()创建子线程的时候,Thread类的init方法里有个if判断:

  • 我们重点看,parent.inheritableThreadLocals != null ,因为inheritableThreadLocals我们在set的时候已经赋值了,所以这里为true。
  • inheritThreadLocalsinit方法的入参,我们很少使用到,这样的方式创建线程Thread(Runnable target, AccessControlContext acc),所以大部分情况都是true。

如果这两个条件都为true的话,那么会将主线程的ThreadLocalMap数据拷贝到子线程的inheritableThreadLocals里。

java 复制代码
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
        ...
        Thread parent = currentThread();
        ...
        // 重点在这里,将主线程的ThreadLocalMap拷贝到子线程的inheritableThreadLocals里。
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        ...

createInheritedMap将主线程的ThreadLocalMap拷贝到子线程里。

java 复制代码
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}

// 将主线程的ThreadLocalMap数据拷贝到子线程里
private ThreadLocalMap(ThreadLocalMap parentMap) {
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    for (int j = 0; j < len; j++) {
        Entry e = parentTable[j];
        if (e != null) {
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                Object value = key.childValue(e.value);
                Entry c = new Entry(key, value);
                // 这里需要重新哈希槽位
                int h = key.threadLocalHashCode & (len - 1);
                while (table[h] != null)
                    h = nextIndex(h, len);
                table[h] = c;
                size++;
            }
        }
    }
}

然后子线程get获取的时候,会去从子线程的inheritableThreadLocals取。

java 复制代码
// ThreadLocal#get
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);		// 走InheritableThreadLocal的获取map
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

// InheritableThreadLocal#get
ThreadLocalMap getMap(Thread t) {
   return t.inheritableThreadLocals;
}

现在,聪明的你看懂了InheritableThreadLocal是怎么实现父子线程传递的吗?

那我们引入一个新的问题,如果我用线程池会怎么呢?

java 复制代码
public static void main(String[] args) throws Exception {
    InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
    threadLocal.set("main value 1");
    System.out.println("main thread set value: " + threadLocal.get());
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    executorService.execute(() -> System.out.println("Task1, thread " + Thread.currentThread().getName()+  " get value : " + threadLocal.get()));

    Thread.sleep(1000);

    threadLocal.set("main value 2");	// 主任务修改最新值
    System.out.println("main thread set value: " + threadLocal.get());
    executorService.execute(() -> System.out.println("Task2, thread " + Thread.currentThread().getName()+  " get value : " + threadLocal.get()));

    threadLocal.remove();
    executorService.shutdown();
}

输出结果,聪明的你,一眼就看出来,task2没有get到主线程的最新值。诶嘿,这是为什么呢?

shell 复制代码
main thread set value: main value 1
Task1, thread pool-1-thread-1 get value : main value 1
main thread set value: main value 2
Task2, thread pool-1-thread-1 get value : main value 1 // 没有获取到主线程的最新值

通过上面的原理讲解,我们知道InheritableThreadLocal实现父子线程传递参数,是在创建子线程 的时候,拷贝父线程的ThreadlLocalMap到子线程里。

可是,我们使用了线程池,线程(比如这里的pool-1-thread-1)是复用的,所以不会创造新的线程了,自然不会从主线程中拷贝新的数据来,所以这里的value没有变化。

不要着急,下面主播还会说说怎么解决这个问题的。

TTL实现父子线程传递

TTL 全称是TransmittableThreadLocal(由阿里开源),专门解决 线程池场景下父子线程值传递丢失的问题,是增强版的InheritableThreadLocal

原理省流版 :是通过装饰任务 + 快照机制 实现线程本地变量的跨线程传递。

我们先来看看TTL的解决:

java 复制代码
public static void main(String[] args) throws Exception {
    TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
    threadLocal.set("main value 1");
    System.out.println("main thread set value: " + threadLocal.get());
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    // 需要显示的包装任务
    executorService.execute(TtlRunnable.get( () -> System.out.println("Task1, thread " + Thread.currentThread().getName() + " get value : " + threadLocal.get())));

    Thread.sleep(1000);

    threadLocal.set("main value 2");
    System.out.println("main thread set value: " + threadLocal.get());
    executorService.execute(TtlRunnable.get( () -> System.out.println("Task2, thread " + Thread.currentThread().getName() + " get value : " + threadLocal.get())));

    threadLocal.remove();
    executorService.shutdown();
}

输出结果

shell 复制代码
main thread set value: main value 1
Task1, thread pool-1-thread-1 get value : main value 1
main thread set value: main value 2
Task2, thread pool-1-thread-1 get value : main value 2

⚠️:以下源码阅读,基于TTL版本2.14.2

TTL包装任务

这里,聪明的你,一定会发现,我们向线程池提交任务的时候,它把任务用TtlRunnable包了一层,为什么要这么包呢?

那我们就看看TtlRunnable包完之后,run方法都干了什么吧!

java 复制代码
// TtlRunnable#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);	// 恢复当前线程的本地变量
    }
}

这样就很天才的解决了,子线程没有更新到父线程最新本地变量。(后面会说每一步具体是怎么做的,这里只需要了解个大概的流程就行了)

这个工作流程,是一个很经典的CRR模式:

capture方法:抓取线程(父线程)的所有TTL值。

replay方法:在另一个线程(子线程)中,回放在capture方法中抓取的TTL值,并返回 回放前TTL值的备份

restore方法:恢复子线程执行replay方法之前的TTL值(即备份)

holder

我们先从set方法开始说起吧!因为通过这个,你可以了解到holder

这对后续源码capture相关的阅读很重要。

java 复制代码
@Override
public final void set(T value) {
    if (!disableIgnoreNullValueSemantics && value == null) {
        remove();
    } else {
        super.set(value);	// ThreadLocal的set实现
        addThisToHolder();	// 放入到一个holder里面
    }
}

我们会将当前线程的这个的TransmittableThreadLocal放到一个holder里。

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

这个holder不要觉得很复杂,因为没有WeakHashSet,所以这里才用WeakHashMap。

我们把这个holder当成Set集合来用,存当前线程所有的TransmittableThreadLocal

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

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

Transmitter和Transmittee

这里再补充下TransmitterTransmittee的概念。简单来说就是:

Transmitter管理所有Transmittee

Transmittee负责做具体的CRR操作。

Transmitter 是管理者 :维护所有 Transmittee,触发它们的执行(就是执行CRR操作,具体怎么做,下面的代码会说)。

Transmitter有一个transmitteeSet来存放所有的Transmittee

Transmittee会在注册的时候,被放入transmitteeSet里,如下代码所表示:

java 复制代码
private static final Set<Transmittee<Object, Object>> transmitteeSet = new CopyOnWriteArraySet<>();

static {
    registerTransmittee(ttlTransmittee);	// 注册ttlTransmittee
    registerTransmittee(threadLocalTransmittee); // 注册threadLocalTransmittee
}

// TransmittableThreadLocal.Transmitter#registerTransmittee
// registerTransmittee方法是扩展点,可以注册自己实现的Transmittee
// 注册Transmittee到transmitteeSet里
@SuppressWarnings("unchecked")
public static <C, B> boolean registerTransmittee(@NonNull Transmittee<C, B> transmittee) {
    return transmitteeSet.add((Transmittee<Object, Object>) transmittee);
}

Transmittee 是被管理者:仅实现具体逻辑,不参与全局调度。

先来看看Transmittee接口吧,简单来说就是定义了CRR操作。

java 复制代码
// TransmittableThreadLocal.Transmitter.Transmittee
// C是Transmittee拍快照的类型 , B是Transmittee备份的类型。
public interface Transmittee<C, B> {
    @NonNull
    C capture();

    @NonNull
    B replay(@NonNull C captured);

    @NonNull
    B clear();

    void restore(@NonNull B backup);
}

TTL提供了2种Transmittee的实现,并且会自动注册的。

java 复制代码
static {
    registerTransmittee(ttlTransmittee);	// 注册ttlTransmittee
    registerTransmittee(threadLocalTransmittee); // 注册threadLocalTransmittee
}

// TransmittableThreadLocal.Transmitter#ttlTransmittee
private static final Transmittee<HashMap<TransmittableThreadLocal<Object>, Object>, HashMap<TransmittableThreadLocal<Object>, Object>> ttlTransmittee =
        new Transmittee<HashMap<TransmittableThreadLocal<Object>, Object>, HashMap<TransmittableThreadLocal<Object>, Object>>() {
...

// TransmittableThreadLocal.Transmitter#threadLocalTransmittee
private static final Transmittee<HashMap<ThreadLocal<Object>, Object>, HashMap<ThreadLocal<Object>, Object>> threadLocalTransmittee =
        new Transmittee<HashMap<ThreadLocal<Object>, Object>, HashMap<ThreadLocal<Object>, Object>>() {
...

这里我们简单来说说这两个ttlTransmitteethreadLocalTransmittee有啥区别吧。

  • ttlTransmittee:就是用于处理我们TransmittableThreadLocal传递数据。
  • threadLocalTransmittee:用于,不好把ThreadLocal改造成TransmittableThreadLocal传递数据的场景。

capture

有了对holder的了解,现在我们对拍快照就会更容易看懂,所以,现在,跟上主播的节奏,👀让我们看看是怎么拍快照的。

TtlRunnable.get()的时候。

java 复制代码
@Nullable
@Contract(value = "null -> null; !null -> !null", pure = true)
public static TtlRunnable get(@Nullable Runnable runnable) {
    return get(runnable, false, false);
}


@Nullable
@Contract(value = "null, _, _ -> null; !null, _, _ -> !null", pure = true)
public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
    if (runnable == null) return null;

    if (runnable instanceof TtlEnhanced) {
        // avoid redundant decoration, and ensure idempotency
        if (idempotent) return (TtlRunnable) runnable;
        else throw new IllegalStateException("Already TtlRunnable!");
    }

    // 重点在new TtlRunnable构造方法
    return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
}

// TtlRunnable的构造方法,这里会捕获快照
private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
    this.capturedRef = new AtomicReference<>(capture());	// 捕获父线程本地变量
    this.runnable = runnable;
    this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}

首先是到Transmitter(管理者),它是怎么捕获的呢:

  1. Transmitter(管理者)遍历所有的Transmittee
  2. 调用每个Transmittee的具体捕获逻辑,并将具体的捕获结果。
  3. 将所有的Transmittee和捕获数据,存入一个Map(Key是Transmittee,Value是这个Transmittee捕获逻辑的返回。),再封装成快照返回。

主播也是尽力还原了Snapshot大概长什么样:

java 复制代码
// TransmittableThreadLocal.Transmitter#capture
@NonNull
public static Object capture() {
    final HashMap<Transmittee<Object, Object>, Object> transmittee2Value = newHashMap(transmitteeSet.size());
    // 遍历所有的Transmittee,执行具体的capture逻辑。
    for (Transmittee<Object, Object> transmittee : transmitteeSet) {
        try {
            // 执行每个transmittee具体的capture逻辑,并放入map中。
            transmittee2Value.put(transmittee, transmittee.capture());
        } catch (Throwable t) {
            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING, "exception when Transmitter.capture for transmittee " + transmittee +
                        "(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
            }
        }
    }
    // 返回快照
    return new Snapshot(transmittee2Value);
}

// 快照,重点是transmittee2Value
// transmittee2Value key:Transmittee value:这个Transmittee捕获的数据
private static class Snapshot {
    final HashMap<Transmittee<Object, Object>, Object> transmittee2Value;

    public Snapshot(HashMap<Transmittee<Object, Object>, Object> transmittee2Value) {
        this.transmittee2Value = transmittee2Value;
    }
}

这里我们看匿名类型类,ttlTransmittee具体capture方法拍取快照:

java 复制代码
@NonNull
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> capture() {
    final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = newHashMap(holder.get().size());
    // 遍历holder中存储的TransmittableThreadLocal,并作为key
    // value是TransmittableThreadLocal对应Entry的value
    for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
        ttl2Value.put(threadLocal, threadLocal.copyValue());
    }
    return ttl2Value;
}

// TransmittableThreadLocal#copyValue
private T copyValue() {
    return copy(get());
}

// TransmittableThreadLocal#copy
// 这也是一个扩展点,默认就是返回从TTL get到值。
public T copy(T parentValue) {
    return parentValue;
}    

ok了,快照就这样拍下来了,至此CRR模式的,C(capture)就结束了。

replay

下面我们来看看replay做了什么?

java 复制代码
// TtlRunnable#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);	
    }
}

首先又是到我们的Transmitter(管理者)执行replay逻辑:

  1. 获取新数据的快照。
  2. 遍历Transmittee,执行具体的回放逻辑。
  3. 保存下每个Transmittee,执行replay方法,返回的数据(一般是回放执行前的本地变量),封装完快照返回。
java 复制代码
@NonNull
public static Object replay(@NonNull Object captured) {
    // 获取新数据的快照
    final Snapshot capturedSnapshot = (Snapshot) captured;

    // transmittee2Value是用来存放,回放前的本地变量,这个是为了方便后续的恢复。
    final HashMap<Transmittee<Object, Object>, Object> transmittee2Value = newHashMap(capturedSnapshot.transmittee2Value.size());
    
    // 遍历快照
    for (Map.Entry<Transmittee<Object, Object>, Object> entry : capturedSnapshot.transmittee2Value.entrySet()) {
        Transmittee<Object, Object> transmittee = entry.getKey();
        try {
            Object transmitteeCaptured = entry.getValue();
            // transmittee具体执行回放逻辑。
            transmittee2Value.put(transmittee, transmittee.replay(transmitteeCaptured));
        } catch (Throwable t) {
            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING, "exception when Transmitter.replay for transmittee " + transmittee +
                        "(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
            }
        }
    }

    // 返回快照,这个快照的数据,是回放之前,本地变量的数据
    return new Snapshot(transmittee2Value);
}

那么,让我们看看ttlTransmittee是怎么做的吧!简单流程就是:

  1. 备份旧的本地变量
  2. 更新新的的本地变量
java 复制代码
@NonNull
@Override
public HashMap<TransmittableThreadLocal<Object>, Object> replay(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
    final HashMap<TransmittableThreadLocal<Object>, Object> backup = newHashMap(holder.get().size());

    // 遍历当前线程下,holder里所有的TTL
    for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
        TransmittableThreadLocal<Object> threadLocal = iterator.next();

        // 回放前的本地变量,放入map中
        backup.put(threadLocal, threadLocal.get());

        // 避免更新前后TTL数据不一致
        if (!captured.containsKey(threadLocal)) {
            iterator.remove();
            threadLocal.superRemove();
        }
    }

    // 更新本地变量,为入参的快照
    setTtlValuesTo(captured);

    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());
    }
}

restore

下面我们再来看看restore做了什么?

java 复制代码
@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);	
    }
}

首先又是到我们的Transmitter(管理者)执行restore逻辑,其实已经很简单了:

  1. 遍历Transmittee,执行具体的恢复逻辑。
less 复制代码
public static void restore(@NonNull Object backup) { 
    for (Map.Entry<Transmittee<Object, Object>, Object> entry : ((Snapshot) backup).transmittee2Value.entrySet()) {
        Transmittee<Object, Object> transmittee = entry.getKey();
        try {
            Object transmitteeBackup = entry.getValue();
            // 具体恢复逻辑,入参是备份。
            transmittee.restore(transmitteeBackup);
        } catch (Throwable t) {
            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING, "exception when Transmitter.restore for transmittee " + transmittee +
                        "(class " + transmittee.getClass().getName() + "), just ignored; cause: " + t, t);
            }
        }
    }
}

那么,让我们看看ttlTransmittee是怎么做的吧!简单流程就是:

  1. 恢复本地变量
java 复制代码
@Override
public void restore(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) {
    
    doExecuteCallback(false);

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

        // 避免恢复之后和之前的数据不一致
        if (!backup.containsKey(threadLocal)) {
            iterator.remove();
            threadLocal.superRemove();
        }
    }

    // 恢复之前的本地变量,这个方法,在replay的时候也用到过。
    setTtlValuesTo(backup);
}

后话

怎么样,聪明的你是否对线程之间传递数据,有更多的了解了勒?

比如InheritableThreadLocal是怎么实现父子线程传递的?为什么使用了线程池,传递数据就有问题呢? TTL具体又是怎么解决这个问题的呢?

😄聪明的你,一定也有有了答案。

参考

GitHub - alibaba/transmittable-thread-local: 📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.

相关推荐
异常君3 分钟前
深入 JVM:线程池源码剖析与性能调优全攻略
java·jvm·后端
Light___mmm3 分钟前
注入Java Bean的方式
java
HelloDam3 分钟前
912. 排序数组 超级通俗易懂、全面的快速排序教程(优化重复元素、实例有序问题)
后端·算法·排序算法
不怕天黑5 分钟前
浅入浅出redisson分布式锁
后端
一只拉古5 分钟前
使用正则表达式解决问题:从 LeetCode 到生产环境
后端·正则表达式·代码规范
lamdaxu8 分钟前
Docker高级篇
后端
HelloDam10 分钟前
leetcode51.N 皇后 回溯算法求解 + 效率优化
后端·算法
xcLeigh12 分钟前
HTML5好看的水果蔬菜在线商城网站源码系列模板4
java·前端·源码·html5
DataFunTalk16 分钟前
30位数据科学家集结完毕,揭晓大模型时代数据科学的“晋级之路”
前端·后端·算法