1

Zero preparation

0 FBI WARNING

The article is extremely long-winded and devious.

1 What is TransmittableThreadLocal

When developers need to pass certain parameters in the threads of the thread pool, JDK's ThreadLocal is difficult to implement, and static variables will face problems such as inflexibility and thread safety.
TransmittableThreadLocal is Alibaba's open source toolkit to solve this problem.

2 version

  • jdk version
    Azul JDK 17.0.2
  • transmittable-thread-local

     <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>transmittable-thread-local</artifactId>
      <version>2.13.0-Beta1</version>
    </dependency>
  • junit-jupiter

     <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.8.2</version>
    </dependency>

    A Demo

     import com.alibaba.ttl.TransmittableThreadLocal;
    import com.alibaba.ttl.threadpool.TtlExecutors;
    import org.junit.jupiter.api.Test;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TreadLocalTest {
    
      @Test
      public void transmittableThreadLocal() {
    
          TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>();
    
          tl.set(6);
          System.out.println("父线程获取数据:" + tl.get()); // 第一次输出:6
    
          // 使用 jdk 的 Executors 工具创建一个线程池
          // 注意,这个线程池里只有一个线程
          Executor realPool = Executors.newFixedThreadPool(1);
          
          // 使用 TtlExecutors 创建一个 Ttl 框架封装的线程池
          Executor pool = TtlExecutors.getTtlExecutor(realPool);
    
          // 使用线程池跑一个任务
          pool.execute(() -> {
              Integer i = tl.get();
              System.out.println("第一次获取数据:" + i); // 第二次输出:6
          });
    
          // 修改一下 tl 里的值,并再跑一次任务
          tl.set(7);
          pool.execute(() -> {
              Integer i = tl.get();
              System.out.println("第二次获取数据:" + i); // 第三次输出:7
          });
      }
    }

    Second, let's start with InheritableThreadLocal

    1 Thread

    InheritableThreadLocal is a subclass of ThreadLocal that comes with jdk. In the Thread object of jdk, it has separate support.
    First look at the construction method of Thread:

     // java.lang.Thread 的核心构造方法
    private Thread(ThreadGroup g, Runnable target, String name,
                     long stackSize, AccessControlContext acc,
                     boolean inheritThreadLocals) {
      
      // 此处省略一大段无关代码...
      
      // inheritThreadLocals 是一个 boolean 类型的值,是一个 “是否启用 inheritableThreadLocals” 的开关
      // parent 是创造此线程的父线程
      if (inheritThreadLocals && parent.inheritableThreadLocals != null)
          // 如果父线程的 inheritableThreadLocals 存在,则此处会将它挪到当前线程里
          // ThreadLocal.createInheritedMap 是一个深拷贝,会创建新的 Entry
          this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
      
      // 此处省略一大段无关代码...
    }

    2 ThreadLocalMap

    Take a look at ThreadLocal.createInheritedMap:

     // java.lang.ThreadLocal
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
      return new ThreadLocalMap(parentMap);
    }

    This method will create a ThreadLocalMap, and then trace the constructor of the ThreadLocalMap:
    (It is worth noting that ThreadLocalMap is the inner class of ThreadLocal, so the code logic is still in ThreadLocal.java)

     // java.lang.ThreadLocal
    private ThreadLocalMap(ThreadLocalMap parentMap) {
      Entry[] parentTable = parentMap.table;
      int len = parentTable.length;
      setThreshold(len);
      table = new Entry[len];
    
      // 此处把 ThreadLocalMap 里的元素都遍厉一遍
      // 然后都创建成新的 Entry 并塞到新的 ThreadLocalMap 里
      for (Entry e : parentTable) {
          if (e != null) {
              // 此处获取了 Entry 的 key,本质上就是 ThreadLocal 本身
              ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
              if (key != null) {
                  // 用 key 取获取 value,这行代码重点关注,下文会提到
                  Object value = key.childValue(e.value);
                  // 此处创建新的 Entry
                  Entry c = new Entry(key, value);
                  // 处理 hash 碰撞问题并存入
                  int h = key.threadLocalHashCode & (len - 1);
                  while (table[h] != null)
                      h = nextIndex(h, len);
                  table[h] = c;
                  size++;
              }
          }
      }
    }

    3 childValue

    Here is one line of code to focus on:

     Object value = key.childValue(e.value);

    This method is in ThreadLocal:

     // java.lang.ThreadLocal
    T childValue(T parentValue) {
      throw new UnsupportedOperationException();
    }

    As can be seen from the above, this is a reserved template method that is not implemented. It is implemented in InheritableThreadLocal:

     // java.lang.InheritableThreadLocal
    protected T childValue(T parentValue) {
      return parentValue;
    }

    5 initialValue

    initialValue is also an empty method provided by ThreadLocal:

     // java.lang.ThreadLocal
    protected T initialValue() {
      return null;
    }

    This method will be used in the get() method of ThreadLocal:

     // step 1
    // java.lang.ThreadLocal
    public T get() {
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
      if (map != null) {
          ThreadLocalMap.Entry e = map.getEntry(this);
          if (e != null) {
              // 如果 Entry 存在,则此处会返回 Entry 的 value
              T result = (T)e.value;
              return result;
          }
      }
      // 如果 Entry 不存在,或者 ThreadLocalMap 不存在,会在这里初始化一个 value
      // 这个方法见 step 2
      return setInitialValue();
    }
    
    // step 2
    // java.lang.ThreadLocal
    private T setInitialValue() {
      // 这里初始化一个值
      T value = initialValue();
      Thread t = Thread.currentThread();
      ThreadLocalMap map = getMap(t);
      if (map != null) {
          // 将初始化出来的值存进去
          map.set(this, value);
      } else {
          // 初始化 ThreadLocalMap
          createMap(t, value);
      }
      
      // 此处忽略这段代码
      if (this instanceof TerminatingThreadLocal) {
          TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
      }
      
      // 返回
      return value;
    }

    5 The role and problems of InheritableThreadLocal

    Assuming that Thread A is the parent thread of Thread B, it can be seen from the above code:

  • The data in A's InheritableThreadLocal can be inherited by B
  • The inheritance method is to copy the elements in InheritableThreadLocal directly in the constructor when B is created
  • copy is a snapshot mechanism. Once it is over, if you modify the elements in InheritableThreadLocal in A, it will not be synchronized to B.

So here comes the question:
If the system needs to achieve real-time synchronization of InheritableThreadLocal of A and B, how should it be solved?

Three TransmittableThreadLocal

First look at the following three lines of code:

 // 创建一个 TransmittableThreadLocal
ThreadLocal<Integer> tl = new TransmittableThreadLocal<>();

tl.set(6);

Integer i = tl.get();

1 Constructor

The constructor of TransmittableThreadLocal is very simple.

 // 是否要忽略 null value,如果这个参数为 false,则哪怕 value 是 null,也会存储下来
private final boolean disableIgnoreNullValueSemantics;

// 这个参数默认为 false
public TransmittableThreadLocal() {
    this(false);
}


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

2 holders

holder is a static member variable of TransmittableThreadLocal, which is an InheritableThreadLocal.

 // com.alibaba.ttl.TransmittableThreadLocal
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
    new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
    
    // 复写这个方法应该没有别的深意,只是为了防止在调用 holder.get().xxx() 的时候报空指针
    // 应该是开发人员觉得这样比较优雅
    @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);
    }
};

From the above it can be seen that:

  • holder is a record whose value is InheritableThreadLocal of WeakHashMap<TransmittableThreadLocal>
  • The value of WeakHashMap is not used, it can be regarded as a WeakHashSet
  • holder overrides the initialValue and childValue methods

The most important method of holder is addThisToHolder:

 // com.alibaba.ttl.TransmittableThreadLocal
// 如果当前 TransmittableThreadLocal 没有被记录在 holder 中,则会在此处 put 进去
private void addThisToHolder() {
    if (!holder.get().containsKey(this)) {
        holder.get().put((TransmittableThreadLocal<Object>) this, null); // WeakHashMap supports null value.
    }
}

There is also a removal method:

 // com.alibaba.ttl.TransmittableThreadLocal
private void removeThisFromHolder() {
    holder.get().remove(this);
}

3 sets

The method to store the value in.

 // com.alibaba.ttl.TransmittableThreadLocal
@Override
public final void set(T value) {
    if (!disableIgnoreNullValueSemantics && null == value) {
        // 如果 value 是 null,且不忽略 null value,则此处进入删除逻辑
        remove();
    } else {
        // 存储逻辑
        super.set(value);
        // 将当前的 TransmittableThreadLocal 注册到 holder 里
        addThisToHolder();
    }
}

4 get

The method to get the value.

 // com.alibaba.ttl.TransmittableThreadLocal
@Override
public final T get() {
    T value = super.get();
    // 尝试注册到 holder
    if (disableIgnoreNullValueSemantics || null != value) 
        addThisToHolder();
    return value;
}

5 Snapshots

Snapshot is an internal class of TransmittableThreadLocal, used to store ThreadLocal and TransmittableThreadLocal data in the current thread.

 // com.alibaba.ttl.TransmittableThreadLocal
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;
    }
}

6 Transmitter

Transmitter is an inner class of TransmittableThreadLocal, which is essentially a set of static tools.

6.1 Take a snapshot

 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
public static Object capture() {
    // captureTtlValues()  会将当前线程的 TransmittableThreadLocal 数据做成一个 HashMap
    // captureThreadLocalValues() 会将当前线程的 ThreadLocal 数据做成一个 HashMap
    return new Snapshot(captureTtlValues(), captureThreadLocalValues());
}
6.1.1 Get all TransmittableThreadLocal data in holder
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
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;
}
6.1.2 Get all ThreadLocal data in threadLocalHolder
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
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;
}

6.2 Replay

6.2.1 replay
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
// 本质上是对一个 snapshot 进行拷贝
public static Object replay(Object captured) {
    final Snapshot capturedSnapshot = (Snapshot) captured;
    return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value), replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
}
6.2.2 replayTtlValues
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
// 本质上是对一个 map 进行深拷贝
private static HashMap<TransmittableThreadLocal<Object>, Object> replayTtlValues(HashMap<TransmittableThreadLocal<Object>, Object> captured) {
    
    // 创建一个新的 map
    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();

        // 将原来的 map 复制到新的 map 中
        backup.put(threadLocal, threadLocal.get());

        // 此处比较 holder 和 captured 的 key
        // 如果对应不一致,则将 holder 里的数据清空
        if (!captured.containsKey(threadLocal)) {
            iterator.remove();
            threadLocal.superRemove();
        }
    }

    // 将 value 和 key 对应起来
    // 这是一个保底纠错逻辑
    setTtlValuesTo(captured);

    // 这是一个暂时没有用的扩展方法
    doExecuteCallback(true);

    return backup;
}
6.2.3 replayThreadLocalValues
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
// 本质上是对一个 map 进行深拷贝
private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(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());

        // threadLocalClearMark 是一个空对象,用于占位
        // 如果此处的 value 就是这个空对象,则此处代表这个 ttl 里的 value 已经被 clear 了
        final Object value = entry.getValue();
        if (value == threadLocalClearMark) 
            threadLocal.remove();
        else 
            threadLocal.set(value);
    }

    return backup;
}

6.3 Recovery

6.3.1 restore
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
// 用快照来恢复当前线程的 ttl 数据
public static void restore(Object backup) {
    final Snapshot backupSnapshot = (Snapshot) backup;
    restoreTtlValues(backupSnapshot.ttl2Value);
    restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
}
6.3.2 restoreTtlValues

This method is similar to the replayTtlValues(...) method

 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
private static void restoreTtlValues(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();
        }
    }

    setTtlValuesTo(backup);
}
6.3.3 restoreThreadLocalValues
 // com.alibaba.ttl.TransmittableThreadLocal.Transmitter
private static void restoreThreadLocalValues(HashMap<ThreadLocal<Object>, Object> backup) {
    for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) {
        final ThreadLocal<Object> threadLocal = entry.getKey();
        threadLocal.set(entry.getValue());
    }
}

Four ExecutorTtlWrapper

1 ExecutorTtlWrapper

ExecutorTtlWrapper has very little code:

 // com.alibaba.ttl.threadpool.ExecutorTtlWrapper
class ExecutorTtlWrapper implements Executor, TtlWrapper<Executor>, TtlEnhanced {
    
    // 这个变量代表了一个线程池
    private final Executor executor;
    // 这个变量是一个幂等标识符
    protected final boolean idempotent;

    ExecutorTtlWrapper(Executor executor, boolean idempotent) {
        this.executor = executor;
        this.idempotent = idempotent;
    }

    @Override
    public void execute(Runnable command) {
        executor.execute(TtlRunnable.get(command, false, idempotent));
    }

    @Overrid
    public Executor unwrap() {
        return executor;
    }

    // 其它方法不重要,这里省略...
}

ExecutorTtlWrapper is essentially a thread pool proxy. When executing the execute(...) method, it wraps the Runnable task into a TtlRunnable.

2 TtlEnhanced

 // 这是一个单纯的空接口,用来标识一个类
public interface TtlEnhanced {
    
}

3 TtlWrapper

 // TtlWrapper 用来标识一个包装类
// 需要实现获取被包装对象的 unwrap 方法
public interface TtlWrapper<T> extends TtlEnhanced {
    T unwrap();
}

4 TtlExecutors

TtlExecutors is a static utility class used to generate ExecutorTtlWrapper.

 // com.alibaba.ttl.threadpool.TtlExecutors
public static Executor getTtlExecutor(Executor executor) {
    // 如果已经包装过了,那么此处直接返回
    if (TtlAgent.isTtlAgentLoaded() || null == executor || executor instanceof TtlEnhanced) {
        return executor;
    }
    
    // 如果没有包装过,那么此处包装一下
    // 幂等标识符,此处默认为 true
    return new ExecutorTtlWrapper(executor, true);
}

TtlAgent is the application of probe technology and will not be explained for the time being.

Five TtlRunnable

1 TtlRunnable

First look at the class:

 // com.alibaba.ttl.TtlRunnable
public final class TtlRunnable implements Runnable, TtlWrapper<Runnable>, TtlEnhanced, TtlAttachments {
    
    private final AtomicReference<Object> capturedRef;
    private final Runnable runnable;
    private final boolean releaseTtlValueReferenceAfterRun;
    
    private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
        // capture() 方法见上面 第三 part 的 Transmitter 部分
        // 本质上这是当前线程所存储的 TransmittableThreadLocal 和 ThreadLocal 的快照
        this.capturedRef = new AtomicReference<Object>(capture());
        // 真实的业务逻辑
        this.runnable = runnable;
        // 当前 TtlRunnable 是否可以重复执行
        // true 的情况下,只要执行完,就不能重复执行了
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
    }
    
    // 其它方法先省略...   
}

2 get

TtlRunnable.get(...) is a static method that creates a TtlRunnable object.

 // com.alibaba.ttl.TtlRunnable
public static TtlRunnable get(Runnable runnable) {
    return get(runnable, false, false);
}

public static TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
    return get(runnable, releaseTtlValueReferenceAfterRun, false);
}

public static TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) {
    // 空判断
    if (null == runnable) 
        return null;

    // 如果当前为幂等,则此处复用
    if (runnable instanceof TtlEnhanced) {
        if (idempotent) 
            return (TtlRunnable) runnable;
        else 
            throw new IllegalStateException("Already TtlRunnable!");
    }
    
    // 创建对象
    return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun);
}

3 runs

TtlRunnable.run() is the core method and encapsulates business logic.

 // com.alibaba.ttl.TtlRunnable
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!");
    }

    // replay 方法来自 Transmitter
    // 用于创建一个当前线程的 ThreadLocal 的备份
    final Object backup = replay(captured);
    try {
        runnable.run();
    } finally {
        // restore 方法来自 Transmitter
        // 使用备份来恢复当前线程的 ThreadLocal 数据
        restore(backup);
    }
}

captured is actually a memo pattern used to ensure that data modifications within the child thread do not affect the parent thread.

six o'clock nagging

  • The packaging is very interesting, but I still don't understand many details.
  • It is only a personal study note, there may be errors or unclear places, and it is necessary to add

三流
57 声望16 粉丝

三流程序员一枚,立志做保姆级教程。