【源码】【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.

相关推荐
xcya5 分钟前
Java ReentrantLock 核心用法
后端
用户4665370150518 分钟前
如何在 IntelliJ IDEA 中可视化压缩提交到生产分支
后端·github
小楓120124 分钟前
MySQL數據庫開發教學(一) 基本架構
数据库·后端·mysql
天天摸鱼的java工程师26 分钟前
Java 解析 JSON 文件:八年老开发的实战总结(从业务到代码)
java·后端·面试
白仑色27 分钟前
Spring Boot 全局异常处理
java·spring boot·后端·全局异常处理·统一返回格式
之诺33 分钟前
MySQL通信过程字符集转换
后端·mysql
喵手34 分钟前
反射机制:你真的了解它的“能力”吗?
java·后端·java ee
用户4665370150535 分钟前
git代码压缩合并
后端·github
武大打工仔39 分钟前
从零开始手搓一个MVC框架
后端
开心猴爷44 分钟前
移动端网页调试实战 Cookie 丢失问题的排查与优化
后端