One, why?

Pass threadLocal to the thread in the thread pool.

Two, how to use?

Use TtlRunnable and TtlCallable to decorate the Runnable and Callable of the incoming thread pool.

Sample code:

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / setting in the parent thread context. The set (" value - set - in - the parent "); Runnable task = new RunnableTask(); TtlRunnable Runnable ttlRunnable = ttLRunnable.get (task); executorService.submit(ttlRunnable); / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / Task can be read, The value is "value-set-in-parent" String value = context.get();Copy the code

Runnable is demonstrated above, and Callable is handled similarly

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / setting in the parent thread context. The set (" value - set - in - the parent "); Callable call = new CallableTask(); TtlCallable ttlCallable = ttlCallable. Get (call); executorService.submit(ttlCallable); / / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = / / to read in the Call, The value is "value-set-in-parent" String value = context.get();Copy the code

Three, the core source code implementation?

With the Runnable example above, let’s look at the implementation of TransmittableThreadLocal.

Context with TransmittableThreadLocal = new TransmittableThreadLocal<>();

First look at the signature of the TransmittableThreadLocal class:

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> implements TtlCopier<T>
Copy the code

If the class TransmittableThreadLocal inherits from InheritableThreadLocal, the InheritableThreadLocal will have the content TransmittableThreadLocal. If the class TransmittableThreadLocal InheritableThreadLocal will have the content TransmittableThreadLocal. Let’s move on to TtlCopier:

@FunctionalInterface
public interface TtlCopier<T> {
    T copy(T parentValue);
}
Copy the code

As seen above, TtlCopier is a functional interface.

The TransmittableThreadLocal constructor is called on the first line of the sample code, so what does the TransmittableThreadLocal constructor have?

public TransmittableThreadLocal() {
    this(false);
}


public TransmittableThreadLocal(boolean disableIgnoreNullValueSemantics) {
    this.disableIgnoreNullValueSemantics = disableIgnoreNullValueSemantics;
}


private final boolean disableIgnoreNullValueSemantics;
Copy the code

As we saw above, it is a simple constructor, nothing else.

3.2 the context. The set (” value – set – in – the parent “);

Then look at the second line of the sample code: context.set(“value-set-in-parent”);

So let’s see what the set method with TransmittableThreadLocal does.

@Override
public final void set(T value) {
    if (!disableIgnoreNullValueSemantics && null == value) {
        // may set null to remove value
        remove();
    } else {
        super.set(value);
        addThisToHolder();
    }
}
Copy the code

If disableIgnoreNullValueSemantics = = false, and null = = value is the remove (),

Super.set (value); – > addThisToHolder();

super.set(value); We’re pretty familiar with this, so let’s look at addThisToHolder(); Do what?

@SuppressWarnings("unchecked") private void addThisToHolder() { if (! holder.get().containsKey(this)) { holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value. } }Copy the code

I see a variable called holder, what is this?

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); }};Copy the code

When we use a static InheritableThreadLocal variable to store a WeakHashMap, then when we use a WeakHashMap, The key is TransmittableThreadLocal, and the value is Object.

Then I see its childValue method, which tells me that if this InheritableThreadLocal is passed to a child thread, its value is re-new, not just passing a reference.

addThisToHolder 
Copy the code

Holder.get () returns in the current thread

ThreadLocal. ThreadLocalMap inheritableThreadLocals variables stored in the WeakHashMap, if WeakHashMap does not exist in accordance with the above:

@Override protected WeakHashMap<TransmittableThreadLocal<Object>, ? > initialValue() { return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(); }Copy the code

If the current TransmittableThreadLocal is not included in the WeakHashMap, the current TransmittableThreadLocal will be put into the WeakHashMap and the value will be null.

So what exactly is addThisToHolder?

As the name suggests: Add this TransmittableThreadLocal to the holder. If the Java process has any TransmittableThreadLocal issues, you can check the holder.

The holder is also an InheritableThreadLocal that maintains a WeakHashMap. The WeakHashMap is used to collect all the TransmittableThreadLocal issues in the thread.

The memory structure is shown in Figure 1 below:

Ps: here briefly explain: ThreadLocal ThreadLocalMap and difference in the WeakHashMap? ThreadLocalMap is a simple map that uses shift to resolve hash conflicts, while WeakHashMap is a simple map that uses linked lists to resolve hash conflicts.

Similarity ThreadLocalMap.Entry and WeakHashMap both inherit weak references, and the reference of weak references points to keys. ThreadLocalMap.Entry:

static class Entry extends WeakReference<ThreadLocal<? >> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; }}Copy the code

WeakHashMap.Entry:

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    V value;
    final int hash;
    Entry<K,V> next;
Copy the code

If there is any set method with TransmittableThreadLocal, what does the remove() method do?

@Override
public final void remove() {
    removeThisFromHolder();
    super.remove();
}
Copy the code

super.remove(); We’re done. We’re all familiar. So follow this with removeThisFromHolder();

private void removeThisFromHolder() {
    holder.get().remove(this);
}
Copy the code

As shown above, we take out the WeakHashMap in the current thread from the holder and delete the TransmittableThreadLocal corresponding to the WeakHashMap.

If there is any set method that has issues with TransmittableThreadLocal, we have completed the set method if there is any TransmittableThreadLocal.

First if disableIgnoreNullValueSemantics is false (the default is false), and value = null, at this time will call InheritableThreadLocal the remove method, corresponding Entry will be deleted,

Otherwise, the InheritableThreadLocal set method is called; Then remove or set, after finish, need to maintenance of holder, because of the holder, this variable is used to record, the current thread ThreadLocal. ThreadLocalMap inheritableThreadLocals variables, There is any TransmittableThreadLocal. This is very intuitive, as shown in Figure 1.

3.3 Runnable ttlRunnable = ttLRunnable.get (task);

First look at the TtlRunnable signature, look at the get method; TtlRunnable’s signature and member variables are as follows:

public final class TtlRunnable implements Runnable, TtlWrapper<Runnable>, TtlEnhanced, TtlAttachments {
    private final AtomicReference<Object> capturedRef;
    private final Runnable runnable;
    private final boolean releaseTtlValueReferenceAfterRun;
Copy the code

Then follow the interface it implements:

First TtlEnhanced:

public interface TtlEnhanced {
}
Copy the code

Empty. The purpose of an empty interface is generally to label other classes.

Take a look at TtlAttachments:

public interface TtlAttachments extends TtlEnhanced {
    void setTtlAttachment(@NonNull String key, Object value);
    <T> T getTtlAttachment(@NonNull String key);
    String KEY_IS_AUTO_WRAPPER = "ttl.is.auto.wrapper";
}
Copy the code

TtlWrapper:

public interface TtlWrapper<T> extends TtlEnhanced { /** * unwrap {@link TtlWrapper} to the original/underneath one. * *  @see TtlUnwrap#unwrap(Object) */ @NonNull T unwrap(); }Copy the code

I don’t know what it’s for? Take a look at the implementation in TtlRunnable:

@NonNull
@Override
public Runnable unwrap() {
    return runnable;
}
Copy the code

Now we know what this interface does, it returns who the decorator decorated. And according to the comment is to return the original decorator.

So what does static get do?

@Nullable
public static TtlRunnable get(@Nullable Runnable runnable) {
    return get(runnable, false, false);
}
Copy the code

Continue to follow:

/** * Factory method, wrap input {@link Runnable} to {@link TtlRunnable}. * * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}. * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred. * @param idempotent is idempotent mode or not. if {@code true}, just return input {@link Runnable} when it's {@link TtlRunnable}, * otherwise throw {@link IllegalStateException}. * <B><I>Caution</I></B>: {@code true} will cover up bugs! <b>DO NOT</b> set, only when you know why. * @return Wrapped {@link Runnable} * @throws IllegalStateException when input is {@link TtlRunnable} already and not idempotent. */ @Nullable public static TtlRunnable get(@Nullable Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) { if (null == runnable) return null; if (runnable instanceof TtlEnhanced) { // avoid redundant decoration, and ensure idempotency if (idempotent) return (TtlRunnable) runnable; else throw new IllegalStateException("Already TtlRunnable!" ); } return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun); }Copy the code

If the input parameter runnable is already an instance of TtlEnhanced, the input parameter Runnable is returned directly if it is an IDEmpotent, otherwise an exception is raised.

Then with:

private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
    this.capturedRef = new AtomicReference<Object>(capture());
    this.runnable = runnable;
    this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}
Copy the code

Capture () :

public static class Transmitter {
    /**
     * Capture all {@link TransmittableThreadLocal} and registered {@link ThreadLocal} values in the current thread.
     *
     * @return the captured {@link TransmittableThreadLocal} values
     * @since 2.3.0
     */
    @NonNull
    public static Object capture() {
        return new Snapshot(captureTtlValues(), captureThreadLocalValues());
    }
Copy the code

The above Transmitter is the static internal class with TransmittableThreadLocal.

Then with:

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#captureTtlValues

private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {
    HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();
    for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
        ttl2Value.put(threadLocal, threadLocal.copyValue());
    }
    return ttl2Value;
}
Copy the code

Follow this: threadlocal.copyValue (); com.alibaba.ttl.TransmittableThreadLocal#copyValue

private T copyValue() {
    return copy(get());
}
Copy the code

Com. Alibaba. TTL. TransmittableThreadLocal# copy:

public T copy(T parentValue) {
    return parentValue;
}
Copy the code

Com. Alibaba. TTL. TransmittableThreadLocal# get:

@Override public final T get() { T value = super.get(); if (disableIgnoreNullValueSemantics || null ! = value) addThisToHolder(); return value; }Copy the code

If the TransmittableThreadLocal () method is used, the InheritableThreadLocal () method will be used. If the TransmittableThreadLocal () method is used, the InheritableThreadLocal () method will be used.

What does captureTtlValues do?

If there is any TransmittableThreadLocal in the current thread, captureTtlValues will be returned as a map with the key of TransmittableThreadLocal. Value is the value of the TransmittableThreadLocal in the current thread, and this value is passed by reference only.

Next to: com. Alibaba. TTL. TransmittableThreadLocal. Transmitter# captureThreadLocalValues

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;
}
Copy the code

As we saw above, captureThreadLocalValues and captureTtlValues do basically the same thing, except that the object is a threadLocalHolder, so let’s see what this threadLocalHolder is.

private static volatile WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> threadLocalHolder = new WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>>();
Copy the code

Com. Alibaba. TTL. TransmittableThreadLocal. Transmitter# threadLocalHolder is a WeakHashMap, the key value for TtlCopier ThreadLocal.

Follow with when to write threadLocalHolder:

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#registerThreadLocal(java.lang.ThreadLocal, com.alibaba.ttl.TtlCopier, boolean)

And com. Alibaba. TTL. TransmittableThreadLocal. Transmitter# unregisterThreadLocal

/** * Register the {@link ThreadLocal}(including subclass {@link InheritableThreadLocal}) instances * to enhance the <b>Transmittable</b> ability for the existed {@link ThreadLocal} instances. * <p> * If the registered {@link ThreadLocal} instance is {@link TransmittableThreadLocal} just ignores and return {@code true}. * since a {@link TransmittableThreadLocal} instance itself has the {@code Transmittable} ability, * it is unnecessary to register a {@link TransmittableThreadLocal} instance. * * <B><I>Caution:</I></B><br> * If the registered {@link ThreadLocal} instance is not {@link InheritableThreadLocal}, * the instance can NOT <B><I>{@code inherit}</I></B> value from parent thread(aka. the <b>inheritable</b> ability)! * * @param threadLocal the {@link ThreadLocal} instance that to enhance the <b>Transmittable</b> ability * @param copier  the {@link TtlCopier} * @param force if {@code true}, update {@code copier} to {@link ThreadLocal} instance * when a {@link ThreadLocal} instance is already registered; otherwise, ignore. * @return {@code true} if register the {@link ThreadLocal} instance and set {@code copier}, otherwise {@code false} * @see #registerThreadLocal(ThreadLocal, TtlCopier) * @since 2.11.0 */ @suppressWarnings ("unchecked") public static <T> Boolean registerThreadLocal(@nonNULL ThreadLocal<T> threadLocal, @NonNull TtlCopier<T> copier, boolean force) { if (threadLocal instanceof TransmittableThreadLocal) { logger.warning("register a TransmittableThreadLocal instance, this is unnecessary!" ); return true; } synchronized (threadLocalHolderUpdateLock) { if (! force && threadLocalHolder.containsKey(threadLocal)) return false; WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> newHolder = new WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>>(threadLocalHolder); newHolder.put((ThreadLocal<Object>) threadLocal, (TtlCopier<Object>) copier); threadLocalHolder = newHolder; return true; } } public static <T> boolean unregisterThreadLocal(@NonNull ThreadLocal<T> threadLocal) { if (threadLocal instanceof TransmittableThreadLocal) { logger.warning("unregister a TransmittableThreadLocal instance, this is unnecessary!" ); return true; } synchronized (threadLocalHolderUpdateLock) { if (! threadLocalHolder.containsKey(threadLocal)) return false; WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>> newHolder = new WeakHashMap<ThreadLocal<Object>, TtlCopier<Object>>(threadLocalHolder); newHolder.remove(threadLocal); threadLocalHolder = newHolder; return true; }}Copy the code

If the threadLocalHolder is used to register existing ThreadLocal and InheritableThreadLocal in old code, and if there is any TransmittableThreadLocal that has issues with context, the user will have to initiate the context TransmittableThreadLocal function.

So captureThreadLocalValues retrieves the ThreadLocal stored in threadLocalHolder (including InheritableThreadLocal) and their values in the current thread;

New Snapshot(captureTtlValues(), captureThreadLocalValues());

com.alibaba.ttl.TransmittableThreadLocal.Transmitter.Snapshot

private static class Snapshot { final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value; final HashMap<ThreadLocal<Object>, Object> threadLocal2Value; private Snapshot(HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value, HashMap<ThreadLocal<Object>, Object> threadLocal2Value) { this.ttl2Value = ttl2Value; this.threadLocal2Value = threadLocal2Value; }}Copy the code

This.capturedref = new AtomicReference(capture());

public AtomicReference(V initialValue) {
    value = initialValue;
}
Copy the code

So far we have seen the AtomicReference capturedRef in TtlRunnable; The Snapshot object that stores all the TransmittableThreadLocal and ThreadLocal (including InheritableThreadLocal) values in the current thread.

Following, since TtlRunnable is then committed to the thread pool, we need to look at the run method for TtlRunnable:

@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); }}Copy the code

Captured == null = null && releaseTtlValueReferenceAfterRun = = true need to perform

CapturedRef.com pareAndSet (captured, null), the value of using cas will AtomicReference to null, ask a question here, why should have AtomicReference here?

If the CAS fails, an exception is thrown.

Follow:

final Object backup = replay(captured); And restore (backup); The value of the parent thread has been replayed into the current thread pool. The value of the parent thread has been replayed into the pool thread

runnable.run(); Restore (backup) after execution; This is an Entry that needs to be replayed and deleted. So let’s go through the code and see if we’re right.

@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>(); for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) { 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)) { iterator.remove(); threadLocal.superRemove(); } } // set TTL values to captured setTtlValuesTo(captured); // call beforeExecute callback doExecuteCallback(true); return backup; }Copy the code

If there is any TransmittableThreadLocal in the current pooled thread, the replayTtlValues function will be initialized

Inside the HashMap < TransmittableThreadLocal, Object > backup;

If there is any TransmittableThreadLocal in the current pooled thread that is not in the parent thread (the thread where runnable is placed), If the TransmittableThreadLocal is deleted from the pooled thread and the holder is maintained, the TransmittableThreadLocal will be deleted from the holder.

Then call:

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#setTtlValuesTo

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

As we saw above, we set the value from the parent thread to the current thread;

After:

 // call beforeExecute callback
    doExecuteCallback(true);
Copy the code

See comments know is the callback function: com. Alibaba. TTL. TransmittableThreadLocal# doExecuteCallback

private static void doExecuteCallback(boolean isBefore) { for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) { try { if (isBefore) threadLocal.beforeExecute(); else threadLocal.afterExecute(); } catch (Throwable t) { if (logger.isLoggable(Level.WARNING)) { logger.log(Level.WARNING, "TTL exception when " + (isBefore ? "beforeExecute" : "afterExecute") + ", cause: " + t.toString(), t); }}}}Copy the code

For all TransmittableThreadLocal issues in the current pooled thread, serially call their beforeExecute() method, currently

Com. Alibaba. TTL. TransmittableThreadLocal# beforeExecute is empty;

protected void beforeExecute() {
}
Copy the code

Then with:

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#replayThreadLocalValues

private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
    final HashMap<ThreadLocal<Object>, Object> backup = new HashMap<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;
}
Copy the code

If value= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

private static final Object threadLocalClearMark = new Object(); The change threadLocal is removed from the pooled thread. The threadLocalClearMark is in doubt, so let’s follow along,

Therefore:

final Object backup = replay(captured);

If there is any TransmittableThreadLocal and ThreadLocal have their values before the pooled thread executes the run method; if there is any TransmittableThreadLocal and ThreadLocal have their values before the pooled thread executes the run method.

And what we’re going to do is

runnable.run();

If the getmethod of the TransmittableThreadLocal and the registered ThreadLocal are called in runnable, the values in their parent thread will be returned to the pooled thread.

This is followed by:

restore(backup);

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#restore

public static void restore(@NonNull Object backup) {
    final Snapshot backupSnapshot = (Snapshot) backup;
    restoreTtlValues(backupSnapshot.ttl2Value);
    restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
}
Copy the code

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#restoreTtlValues

private static void restoreTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) { // call afterExecute callback doExecuteCallback(false); for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) { TransmittableThreadLocal<Object> threadLocal = iterator.next(); // 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); }Copy the code

If there is any TransmittableThreadLocal that has TransmittableThreadLocal, execute the callback function:

protected void afterExecute() {
}
Copy the code

If there is any TransmittableThreadLocal in the pooled thread, delete it if there is no TransmittableThreadLocal in the backup thread and if there is no TransmittableThreadLocal in the holder thread.

After the call

  // restore TTL values
    setTtlValuesTo(backup);
Copy the code

Set the value of TransmittableThreadLocal from the pooled thread to the pooled thread.

Follow along:

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#restoreThreadLocalValues

private static void restoreThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> backup) { for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) { final ThreadLocal<Object> threadLocal = entry.getKey(); threadLocal.set(entry.getValue()); }}Copy the code

Restore the value of threadLocal from the pooled thread.

If there is any source of TransmittableThreadLocal that has any issues with TransmittableThreadLocal, there is no TransmittableThreadLocal.

3.4 summarize

If the parent thread has any thread TransmittableThreadLocal and the value of the registered ThreadLocal, the value of the TransmittableThreadLocal will be captured. If the parent thread has any thread TransmittableThreadLocal, the value of the registered ThreadLocal will be captured.

If the parent thread has any TransmittableThreadLocal and registered ThreadLocal values that are captured from the parent thread, place them into the pooled thread before runnable runs

ThreadLocal. ThreadLocalMap inheritableThreadLocals and cracked, pooling of threads at the same time with the original TransmittableThreadLocal and registered ThreadLocal, And its value, save it,Copy the code
After executing the run method, restore is restored, so restore means restore.Copy the code

1, the capture

2, replay and backup

3, run

4, restore

3.5 QA

3.5.1 Is it ok if the holder data structure is not maintained?

A: No! The holder is an InheritableThreadLocal that maintains a WeakHashMap that collects all the TransmittableThreadLocal issues in the thread. So can we collect it without the holder?

. If we can get the Thread of the ThreadLocal ThreadLocalMap inheritableThreadLocals, then traverse is done in the line, but ThreadLocalMap is visibility, therefore is not in the application code, So the holder must exist.

3.5.2 Is it ok if WeakHashMap is not used in holder?

Answer: No! Because if a TransmittableThreadLocal has strong can’t reach, so at this time the Thread ThreadLocal. ThreadLocalMap inheritableThreadLocals is mechanism can guarantee the corresponding Entry is deleted,

If the TransmittableThreadLocal and its value are gc removed, if WeakHashMap is used instead of WeakHashMap, the TransmittableThreadLocal will always be strongly reachable.

3.5.3 why com. Alibaba. TTL. TtlRunnable# capturedRef with AtomicReference type, need not ok?

Answer: No! If com. Alibaba. TTL. TtlRunnable# releaseTtlValueReferenceAfterRun is true, that is to say, should be clear in TtlRunnable execution after the parent thread snapshot, so this at this time

TtlRunnable cannot be submitted to two threads at the same time. If the TtlRunnable#capturedRef is used to point directly to the Snapshot instead of the AtomicReference

The application executes this TtlRunnable on both threads at the same time, so if the code shown above in the red box is simply executed without CAS, capturedRef=null,

So it will lead to the TtlRunnable can, in the two threads execute simultaneously and won’t have a problem, but we will releaseTtlValueReferenceAfterRun is set to true in order to let a TtlRunnable only run once, then the semantic conflict,

If the code in the red box is running at the same time, there must be a thread in the CAS failed, so the exception will be thrown, ensuring that a TtlRunnable can only run once, this semantics.

3.5.4 Where is threadLocalClearMark used?

com.alibaba.ttl.TransmittableThreadLocal.Transmitter#clear

The code is as follows:

/**
 * Clear all {@link TransmittableThreadLocal} and registered {@link ThreadLocal} values in the current thread,
 * and return the backup {@link TransmittableThreadLocal} values in the current thread before clear.
 *
 * @return the backup {@link TransmittableThreadLocal} values before clear
 * @since 2.9.0
 */
@NonNull
public static Object clear() {
    final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value = new HashMap<TransmittableThreadLocal<Object>, Object>();

    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();
        threadLocal2Value.put(threadLocal, threadLocalClearMark);
    }

    return replay(new Snapshot(ttl2Value, threadLocal2Value));
}
Copy the code

The above code: delete the current thread ThreadLocal. ThreadLocalMap inheritableThreadLocals all TransmittableThreadLocal in Entry,

All ThreadLocal values in the current thread are set to

ThreadLocalClearMark. If the value of a ThreadLocal in the parent thread is threadLocalClearMark, the pooled thread does not need to have a ThreadLocal.Copy the code