Understand the principle of thread pool thread local variable passing through the transmittable thread local source code

premise

In the last month or two, I have spent a lot of time migrating ucloud services and middleware to Alibaba cloud. I don't have much free time to write. I remember the article on source code analysis of ThreadLocal written a long time ago, which mentioned that ThreadLocal has a limitation that variables cannot be passed to pre created threads. A former colleague of HSBC technology Daniel mentioned that the team introduced transmittable thread local to solve this problem. Taking this opportunity, by the way, clone analyzed the source code of transmittable thread local. This article will analyze the limitations of ThreadLocal and inheritable ThreadLocal, and analyze the implementation of the whole framework of transmittable thread local (hereinafter referred to as TTL) from the application of some basic principles and design patterns.

If you are not familiar with thread pool and ThreadLocal, please refer to the preceding article:

It took two weeks to write this article. The writing is hard compared with the price, and there are many words (close to 5W words). I hope to read it with patience.

Variable passing of parent-child thread

There is no explicit API in Java to obtain the parent thread instance based on the child thread instance. A relatively feasible scheme is to obtain the current thread instance when creating the child thread instance. The API used is thread#currentthread():

public class Thread implements Runnable {

    // 省略其他代码

    @HotSpotIntrinsicCandidate
    public static native Thread currentThread();

    // 省略其他代码
}

The thread #currentthread () method is a static local method implemented by the JVM, which is the only API in the JDK that can obtain the parent thread instance. Generally speaking, if you want to get its parent thread instance in the child thread instance, you need to operate as follows:

public class InheritableThread {

    public static void main(String[] args) throws Exception{
        // 父线程就是main线程
        Thread parentThread = Thread.currentThread();
        Thread childThread = new Thread(()-> {
            System.out.println("Parent thread is:" + parentThread.getName());
        },"childThread");
        childThread.start();
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }
}
// 输出结果:
Parent thread is:main

Similarly, if we want to pass a variable instance shared by a parent-child thread, we can do the following:

public class InheritableVars {

    public static void main(String[] args) throws Exception {
        // 父线程就是main线程
        Thread parentThread = Thread.currentThread();
        final Var var = new Var();
        var.setValue1("var1");
        var.setValue2("var2");
        Thread childThread = new Thread(() -> {
            System.out.println("Parent thread is:" + parentThread.getName());
            methodFrame1(var);
        },"childThread");
        childThread.start();
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }

    private static void methodFrame1(Var var) {
        methodFrame2(var);
    }

    private static void methodFrame2(Var var) {

    }

    @Data
    private static class Var {

        private Object value1;
        private Object value2;
    }
}

This approach is actually feasible. All methods in the method stack called by the child thread must display the incoming parameter reference var instance that needs to be passed from the parent thread, which will lead to hard coding problems. It is neither flexible nor reusable. Therefore, the thread local variable is derived. The specific implementations include ThreadLocal and inheritablethreadlocal. The basic principles of the two are similar. In fact, all variable instances are cached in the variable ThreadLocal of the thread instance In threadlocalmap, thread local variable instances are only thread instances to obtain ThreadLocal A bridge of threadlocalmap:

public class Thread implements Runnable {

    // 省略其他代码

    // KEY为ThreadLocal实例,VALUE为具体的值
    ThreadLocal.ThreadLocalMap threadLocals = null;
    
    // KEY为InheritableThreadLocal实例,VALUE为具体的值
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    // 省略其他代码
}

The difference between ThreadLocal and inheritablethreadlocal can be analyzed in combination with the source code (see the next section). If the previous analysis sounds abstract, you can write several classes to deliberate. If the thread is actually called throwablethread and the thread local variable is throwablethreadlocal, the relationship between them is as follows:

public class Actor {

    static ThrowableThreadLocal THREAD_LOCAL = new ThrowableThreadLocal();

    public static void main(String[] args) throws Exception {
        ThrowableThread throwableThread = new ThrowableThread() {

            @Override
            public void run() {
                methodFrame1();
            }
        };
        throwableThread.start();
    }

    private static void methodFrame1() {
        THREAD_LOCAL.set("throwable");
        methodFrame2();
    }

    private static void methodFrame2() {
        System.out.println(THREAD_LOCAL.get());
    }

    /**
     * 这个类暂且认为是java.lang.Thread
     */
    private static class ThrowableThread implements Runnable {

        ThrowableThreadLocal.ThrowableThreadLocalMap threadLocalMap;

        @Override
        public void run() {

        }

        // 这里模拟VM的实现,返回ThrowableThread自身,大家先认为不是返回NULL
        public static ThrowableThread getCurrentThread() {
//            return new ThrowableThread();
            return null;   // <--- 假设这里在VM的实现里面返回的不是NULL而是当前的ThrowableThread
        }

        public void start() {
            run();
        }
    }

    private static class ThrowableThreadLocal {

        public ThrowableThreadLocal() {

        }

        public void set(Object value) {
            ThrowableThread currentThread = ThrowableThread.getCurrentThread();
            assert null != currentThread;
            ThrowableThreadLocalMap threadLocalMap = currentThread.threadLocalMap;
            if (null == threadLocalMap) {
                threadLocalMap = currentThread.threadLocalMap = new ThrowableThreadLocalMap();
            }
            threadLocalMap.put(this,value);
        }

        public Object get() {
            ThrowableThread currentThread = ThrowableThread.getCurrentThread();
            assert null != currentThread;
            ThrowableThreadLocalMap threadLocalMap = currentThread.threadLocalMap;
            if (null == threadLocalMap) {
                return null;
            }
            return threadLocalMap.get(this);
        }
        
        // 这里其实在ThreadLocal中用的是WeakHashMap
        public static class ThrowableThreadLocalMap extends HashMap<ThrowableThreadLocal,Object> {

        }
    }
}

The above code can't run, just explain the principle and relationship through a custom implementation.

Limitations of ThreadLocal and inheritablethreadlocal

Inheritablethreadlocal is a subclass of ThreadLocal. The relationship between them is that both are thread instances to obtain ThreadLocal An intermediate variable of threadlocalmap. The difference is that the two control ThreadLocal The creation time of threadlocalmap and obtaining ThreadLocal through thread instance The corresponding properties of threadlocalmap in the thread instance are different, resulting in a little difference in their functions. Generally speaking, the functional connection and difference between the two are:

public class ThreadLocalMain {

    private static ThreadLocal<String> TL = new ThreadLocal<>();

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            methodFrame1();
        },"childThread").start();
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }

    private static void methodFrame1() {
        TL.set("throwable");
        methodFrame2();
    }

    private static void methodFrame2() {
        System.out.println(TL.get());
    }
}
// 输出结果:
throwable
public class InheritableThreadLocalMain {
    
    // 此处可以尝试替换为ThreadLocal,最后会输出null
    static InheritableThreadLocal<String> ITL = new InheritableThreadLocal<>();

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            // 在父线程中设置变量
            ITL.set("throwable");
            new Thread(() -> {
                methodFrame1();
            },"childThread").start();
        },"parentThread").start();
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }

    private static void methodFrame1() {
        methodFrame2();
    }

    private static void methodFrame2() {
        System.out.println(ITL.get());
    }
}
// 输出结果:
throwable

For the two points mentioned above, please refer to the source code of ThreadLocal, inheritablethreadlocal and thread. Here, the author posts some necessary comments and source code segments:

// --> java.lang.Thread类的源码片段
public class Thread implements Runnable {

    // 省略其他代码 

    // 这是Thread最基本的构造函数
    private Thread(ThreadGroup g,Runnable target,String name,long stackSize,AccessControlContext acc,boolean inheritThreadLocals) {

        // 省略其他代码

        Thread parent = currentThread();
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        // inheritThreadLocals一般情况下为true
        // 当前子线程实例拷贝父线程的inheritableThreadLocals属性,创建一个新的ThreadLocal.ThreadLocalMap实例赋值到自身的inheritableThreadLocals属性
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize;
        this.tid = nextThreadID();
    }

    // 省略其他代码
}

// --> java.lang.ThreadLocal源码片段
public class ThreadLocal<T> {

    // 省略其他代码 

    public void set(T value) {
        Thread t = Thread.currentThread();
        // 通过当前线程获取线程实例中的threadLocals
        ThreadLocalMap map = getMap(t);
        // 线程实例中的threadLocals为NULL,实例则创建一个ThreadLocal.ThreadLocalMap实例添加当前ThreadLocal->VALUE到ThreadLocalMap中,如果已经存在ThreadLocalMap则进行覆盖对应的Entry
        if (map != null) {
            map.set(this,value);
        } else {
            createMap(t,value);
        }
    }

    // 通过线程实例获取该线程的threadLocals实例,其实是ThreadLocal.ThreadLocalMap类型的属性
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }


    public T get() {
        Thread t = Thread.currentThread();
        // 通过当前线程获取线程实例中的threadLocals,再获取ThreadLocal.ThreadLocalMap中匹配上KEY为当前ThreadLocal实例的Entry对应的VALUE
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // 找不到则尝试初始化ThreadLocal.ThreadLocalMap
        return setInitialValue();
    }
    
    // 如果不存在ThreadLocal.ThreadLocalMap,则通过初始化initialValue()方法的返回值,构造一个ThreadLocal.ThreadLocalMap
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this,value);
        else
            createMap(t,value);
        return value;
    }

    // 省略其他代码 
}

// --> java.lang.InheritableThreadLocal源码 - 太简单,全量贴出
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    
    // 这个方法使用在线程Thread的构造函数里面ThreadLocal.createInheritedMap(),基于父线程InheritableThreadLocal的属性创建子线程的InheritableThreadLocal属性,它的返回值决定了拷贝父线程的属性时候传入子线程的值
    protected T childValue(T parentValue) {
        return parentValue;
    }
    
    // 覆盖获取线程实例中的绑定的ThreadLocalMap为Thread#inheritableThreadLocals,这个方法其实是覆盖了ThreadLocal中对应的方法,应该加@Override注解
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    
    // 覆盖创建ThreadLocalMap的逻辑,赋值到线程实例中的inheritableThreadLocals,而不是threadLocals,这个方法其实是覆盖了ThreadLocal中对应的方法,应该加@Override注解
    void createMap(Thread t,T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this,firstValue);
    }
}

It must be noted that the setinitialvalue () method here is very important. A new thread instance will use this setinitialvalue () method to construct a new ThreadLocal when initializing (inheriting the thread local variable of the parent thread for inheritablethreadlocal) or calling threadlocal#set () for the first time Threadlocalmap, the createmap () method will be used directly.

Take the two examples mentioned above and paste a picture to deepen understanding:

Example-1:

Example-2:

ThreadLocal、 The biggest limitation of inheritablethreadlocal is that it cannot pass variables for pre created (Unused) thread instances (to be exact, it is feasible to transfer some scenarios for the first time, but later, because the threads in the thread pool are reused, it is impossible to update or modify the transfer value of variables). The pan thread pool executor system, TimerTask and forkjoinpool are generally created in advance (core) threads, that is, they cannot be used in runnable task instances executed by pre created child threads in the thread pool. For example, the following methods will cause parameter passing failure:

public class InheritableThreadForExecutor {

    static final InheritableThreadLocal<String> ITL = new InheritableThreadLocal<>();
    static final Executor EXECUTOR = Executors.newFixedThreadPool(1);

    public static void main(String[] args) throws Exception {
        ITL.set("throwable");
        EXECUTOR.execute(() -> {
            System.out.println(ITL.get());
        });
        ITL.set("doge");
        EXECUTOR.execute(() -> {
            System.out.println(ITL.get());
        });
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }
}
// 输出结果:
throwable
throwable   # <--- 可见此处参数传递出现异常

The first successful variable transfer is because all child threads in the thread pool are derived from the main thread.

Simple use of TTL

TTL is used in its project readme MD or unit testing in the project are introduced in great detail. First, introduce the dependency com alibaba:transmittable-thread-local:2.11. 4. Here is an example:

// 父-子线程
public class TtlSample1 {

    static TransmittableThreadLocal<String> TTL = new TransmittableThreadLocal<>();

    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            // 在父线程中设置变量
            TTL.set("throwable");
            new Thread(TtlRunnable.get(() -> {
                methodFrame1();
            }),"parentThread").start();
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }

    private static void methodFrame1() {
        methodFrame2();
    }

    private static void methodFrame2() {
        System.out.println(TTL.get());
    }
}
// 输出:
throwable

// 线程池
public class TtlSample2 {

    static TransmittableThreadLocal<String> TTL = new TransmittableThreadLocal<>();
    static final Executor EXECUTOR = Executors.newFixedThreadPool(1);

    public static void main(String[] args) throws Exception {
        TTL.set("throwable");
        EXECUTOR.execute(TtlRunnable.get(() -> {
            System.out.println(TTL.get());
        }));
        TTL.set("doge");
        EXECUTOR.execute(TtlRunnable.get(() -> {
            System.out.println(TTL.get());
        }));
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }
}
// 输出:
throwable
doge

Basic principle of TTL implementation

A large number of delegates are used in TTL design. Delegation is the expression in c# and the design mode of benchmarking Java is the proxy mode. For example:

@Slf4j
public class StaticDelegate {

    public static void main(String[] args) throws Exception {
        new RunnableDelegate(() -> log.info("Hello World!")).run();
    }

    @Slf4j
    @requiredArgsConstructor
    private static final class RunnableDelegate implements Runnable {

        private final Runnable runnable;

        @Override
        public void run() {
            try {
                log.info("Before run...");
                runnable.run();
                log.info("After run...");
            } finally {
                log.info("Finally run...");
            }
        }
    }
}
// 输出结果:
23:45:27.763 [main] INFO club.throwable.juc.StaticDelegate$RunnableDelegate - Before run...
23:45:27.766 [main] INFO club.throwable.juc.StaticDelegate - Hello World!
23:45:27.766 [main] INFO club.throwable.juc.StaticDelegate$RunnableDelegate - After run...
23:45:27.766 [main] INFO club.throwable.juc.StaticDelegate$RunnableDelegate - Finally run...

If the delegate is skilled, it can perform many useful functions. For example, it can count the task execution time based on micrometer, report it to Prometheus, and then use grafana for monitoring and display:

// 需要引入io.micrometer:micrometer-core:${version}
@Slf4j
public class MeterDelegate {

    public static void main(String[] args) throws Exception {
        Executor executor = Executors.newFixedThreadPool(1);
        Runnable task = () -> {
            try {
                // 模拟耗时
                Thread.sleep(1000);
            } catch (Exception ignore) {

            }
        };
        Map<String,String> tags = new HashMap<>(8);
        tags.put("_class","MeterDelegate");
        executor.execute(new MicrometerDelegate(task,"test-task",tags));
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }

    @Slf4j
    @requiredArgsConstructor
    private static final class MicrometerDelegate implements Runnable {

        private final Runnable runnable;
        private final String taskType;
        private final Map<String,String> tags;

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            try {
                runnable.run();
            } finally {
                long end = System.currentTimeMillis();
                List<Tag> tagsList = Lists.newArrayList();
                Optional.ofNullable(tags).ifPresent(x -> x.forEach((k,v) -> {
                    tagsList.add(Tag.of(k,v));
                }));
                Metrics.summary(taskType,tagsList).record(end - start);
            }
        }
    }
}

Theoretically, as long as the thread stack does not overflow, the delegate can be wrapped at unlimited levels, which is a bit like an onion structure. The original target method will be wrapped in the innermost part and finally executed:

    public static void main(String[] args) throws Exception {
        Runnable target = () -> log.info("target");
        Delegate level1 = new Delegate(target);
        Delegate level2 = new Delegate(level1);
        Delegate level3 = new Delegate(level2);
        // ......
    }
    
    @requiredArgsConstructor
    static class Delegate implements Runnable{
        
        private final Runnable runnable;

        @Override
        public void run() {
            runnable.run();
        }
    }

Of course, the more levels of delegation, the more complex the code structure will be, which is not conducive to understanding and maintenance. The onion structure of multi-level delegation, combined with the java reflection API to peel off the dependence on specific method calls, is the general principle of aspect programming in Java. Spring AOP is implemented in this way. If the delegate is combined with agent and bytecode enhancement (ASM, javassist, etc.), it can replace the implementation of the corresponding runnable, callable or general interface during class loading, so that the enhanced function can be completed without perception. In addition, the template method model is also used in TTL, such as:

@Slf4j
public class TemplateMethod {

    public static void main(String[] args) throws Exception {
        Runnable runnable = () -> log.info("Hello World!");
        Template template = new Template(runnable) {
            @Override
            protected void beforeExecute() {
                log.info("BeforeExecute...");
            }

            @Override
            protected void afterExecute() {
                log.info("AfterExecute...");
            }
        };
        template.run();
    }

    @requiredArgsConstructor
    static abstract class Template implements Runnable {

        private final Runnable runnable;

        protected void beforeExecute() {

        }

        @Override
        public void run() {
            beforeExecute();
            runnable.run();
            afterExecute();
        }

        protected void afterExecute() {

        }
    }
}
// 输出结果:
00:25:32.862 [main] INFO club.throwable.juc.TemplateMethod - BeforeExecute...
00:25:32.865 [main] INFO club.throwable.juc.TemplateMethod - Hello World!
00:25:32.865 [main] INFO club.throwable.juc.TemplateMethod - AfterExecute...

Two design patterns are analyzed. The following is a brief understanding of the pseudo code of TTL implementation:

# TTL extends InheritableThreadLocal
# Holder of TTL -> InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>,?>> [? => NULL]
(1)创建一个全局的Holder,用于保存父线程(或者明确了父线程的子线程)的TTL对象,这里注意,是TTL对象,Holder是当作Set使用
(2)(父)线程A中使用了TTL,则所有设置的变量会被TTL捕获
(3)(子)线程B使用了TtlRunnable(Runnable的TTL实现,使用了前面提到的委托,像Callable的实现是TtlCallable),会重放所有存储在TTL中的,来自于线程A的存储变量
(4)线程B重放完毕后,清理线程B独立产生的ThreadLocal变量,归还变TTL的变量

These are the main steps. The script is a little abstract. It will be explained in detail in the next section when analyzing the source code.

Source code analysis of TTL

Main analysis:

TTL frame skeleton

TTL is a very powerful framework. It relies on a small number of classes to realize relatively powerful functions. In addition to the API provided to users, it also provides agent-based and bytecode enhancement to realize the function of imperceptibly enhancing the corresponding classes of universal thread pool, which is amazing. Here, we first analyze the programmatic API, and then briefly analyze the implementation of the agent part. The author read the TTL framework around May Day in 2020, and the latest version is 2.11 4。 The TTL project structure is simple:

- transmittable-thread-local
  - com.alibaba.ttl
   - spi   SPI接口和一些实现
   - threadpool   线程池增强,包括ThreadFactory和线程池的Wrapper等
     - agent   线程池的Agent实现相关
   最外层的包有一些Wrapper的实现和TTL

First look at the SPI package:

- spi
  TtlAttachments
  TtlAttachmentsDelegate
  TtlEnhanced
  TtlWrapper

Ttlenhanced is the identification interface (empty interface) of TTL, which identifies that specific components are enhanced by TTL:

public interface TtlEnhanced {

}

Through the instanceof keyword, you can judge whether the specific implementation is a TTL enhanced component. The ttlwrapper interface inherits from the interface ttlenhanced and is used to mark that the implementation class can unpack to obtain the original instance:

public interface TtlWrapper<T> extends TtlEnhanced {
    
    // 返回解包装实例,实际是就是原始实例
    @NonNull
    T unwrap();
}

The ttlatchments interface is also inherited from the interface ttlenhanced. It is used to add K-V structure attachments to TTL. Ttlatchmentsdelegate is its implementation class, and K-V storage is actually delegated to concurrenthashmap:

public interface TtlAttachments extends TtlEnhanced {
    
    // 添加K-V附件
    void setTtlAttachment(@NonNull String key,Object value);
    
    // 通过KEY获取值
    <T> T getTtlAttachment(@NonNull String key);
    
    // 标识自动包装的KEY,Agent模式会使用自动包装,这个时候会传入一个附件的K-V,其中KEY就是KEY_IS_AUTO_WRAPPER
    String KEY_IS_AUTO_WRAPPER = "ttl.is.auto.wrapper";
}

// TtlAttachmentsDelegate
public class TtlAttachmentsDelegate implements TtlAttachments {

    private final ConcurrentMap<String,Object> attachments = new ConcurrentHashMap<String,Object>();

    @Override
    public void setTtlAttachment(@NonNull String key,Object value) {
        attachments.put(key,value);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getTtlAttachment(@NonNull String key) {
        return (T) attachments.get(key);
    }
}

Because the implementation of TTL covers the universal thread pool executor, executorservice, scheduledexecutorservice Forkjoinpool and TimerTask (components in TTL have been marked as expired, and scheduledexecutorservice is recommended). They have a wide range, so it is impossible to analyze all source codes, and their implementation ideas are basically the same. The author will only select the implementation route of executor for analysis below.

Core class transmittablethreadlocal

Transmittablethreadlocal is the core class of TTL, and the TTL framework is named after this class. First look at its constructor and key attributes:

// 函数式接口,TTL拷贝器
@FunctionalInterface
public interface TtlCopier<T> {
   
    // 拷贝父属性
    T copy(T parentValue);
}

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> implements TtlCopier<T> {

    // 日志句柄,使用的不是SLF4J的接口,而是java.util.logging的实现
    private static final Logger logger = Logger.getLogger(TransmittableThreadLocal.class.getName());
    
    // 是否禁用忽略NULL值的语义
    private final boolean disableIgnoreNullValueSemantics;
    
    // 默认是false,也就是不禁用忽略NULL值的语义,也就是忽略NULL值,也就是默认的话,NULL值传入不会覆盖原来已经存在的值
    public TransmittableThreadLocal() {
        this(false);
    }
    
    // 可以通过手动设置,去覆盖IgnoreNullValue的语义,如果设置为true,则是支持NULL值的设置,设置为true的时候,与ThreadLocal的语义一致
    public TransmittableThreadLocal(boolean disableIgnoreNullValueSemantics) {
        this.disableIgnoreNullValueSemantics = disableIgnoreNullValueSemantics;
    }
    
    // 先忽略其他代码
}

The disableignorenullvaluesemantics property is related. You can view issue157. The specific scenarios will also be described in the analysis method below. Transmittablethreadlocal inherits from inheritablethreadlocal. Its essence is ThreadLocal. How does it ensure that variables can be passed to threads in the thread pool? Then analyze all other methods:

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> implements TtlCopier<T> {
    
    // 拷贝器的拷贝方法实现
    public T copy(T parentValue) {
        return parentValue;
    }

    // 模板方法,留给子类实现,在TtlRunnable或者TtlCallable执行前回调
    protected void beforeExecute() {
    }

    // 模板方法,留给子类实现,在TtlRunnable或者TtlCallable执行后回调
    protected void afterExecute() {
    }

    // 获取值,直接从InheritableThreadLocal#get()获取
    @Override
    public final T get() {
        T value = super.get();
        // 如果值不为NULL 或者 禁用了忽略空值的语义(也就是和ThreadLocal语义一致),则重新添加TTL实例自身到存储器
        if (disableIgnoreNullValueSemantics || null != value) addThisToHolder();
        return value;
    }
    
    @Override
    public final void set(T value) {
        // 如果不禁用忽略空值的语义,也就是需要忽略空值,并且设置的入参值为空,则做一次彻底的移除,包括从存储器移除TTL自身实例,TTL(ThrealLocalMap)中也移除对应的值
        if (!disableIgnoreNullValueSemantics && null == value) {
            // may set null to remove value
            remove();
        } else {
            // TTL(ThrealLocalMap)中设置对应的值
            super.set(value);
            // 添加TTL实例自身到存储器
            addThisToHolder();
        }
    }
   
    // 从存储器移除TTL自身实例,从TTL(ThrealLocalMap)中移除对应的值
    @Override
    public final void remove() {
        removeThisFromHolder();
        super.remove();
    }
    
    // 从TTL(ThrealLocalMap)中移除对应的值
    private void superRemove() {
        super.remove();
    }
    
    // 拷贝值,主要是拷贝get()的返回值
    private T copyValue() {
        return copy(get());
    }
     
    // 存储器,本身就是一个InheritableThreadLocal(ThreadLocal)
    // 它的存放对象是WeakHashMap<TransmittableThreadLocal<Object>,?>类型,而WeakHashMap的VALUE总是为NULL,这里当做Set容器使用,WeakHashMap支持NULL值
    private static 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) {
                    // 注意这里的WeakHashMap总是拷贝父线程的值
                    return new WeakHashMap<TransmittableThreadLocal<Object>,Object>(parentValue);
                }
            };
    
    // 添加TTL自身实例到存储器,不存在则添加策略
    @SuppressWarnings("unchecked")
    private void addThisToHolder() {
        if (!holder.get().containsKey(this)) {
            holder.get().put((TransmittableThreadLocal<Object>) this,null); // WeakHashMap supports null value.
        }
    }
    
    // 从存储器移除TTL自身的实例
    private void removeThisFromHolder() {
        holder.get().remove(this);
    }
    
    // 执行目标方法,isBefore决定回调beforeExecute还是afterExecute,注意此回调方法会吞掉所有的异常只打印日志
    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);
                }
            }
        }
    }
    
    // DEBUG模式下打印TTL里面的所有值
    static void dump(@Nullable String title) {
        if (title != null && title.length() > 0) {
            System.out.printf("Start TransmittableThreadLocal[%s] Dump...%n",title);
        } else {
            System.out.println("Start TransmittableThreadLocal Dump...");
        }

        for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
            System.out.println(threadLocal.get());
        }
        System.out.println("TransmittableThreadLocal Dump end!");
    }
    
    // DEBUG模式下打印TTL里面的所有值
    static void dump() {
        dump(null);
    }

    // 省略静态类Transmitter的实现代码
}

Here, we must remember that the holder is globally static, and it itself is an inheritablethreadlocal (the get () method is also thread isolated). It is actually a bridge for the parent thread to manage all transmittablethreadlocal. Here, consider a single thread example to illustrate the storage architecture of transmittablethreadlocal:

public class TtlSample3 {

    static TransmittableThreadLocal<String> TTL1 = new TransmittableThreadLocal<>();
    static TransmittableThreadLocal<String> TTL2 = new TransmittableThreadLocal<>();
    static TransmittableThreadLocal<String> TTL3 = new TransmittableThreadLocal<>();

    public static void main(String[] args) throws Exception {
        TTL1.set("VALUE-1");
        TTL2.set("VALUE-2");
        TTL3.set("VALUE-3");
    }
}

The example is simplified here and only the single thread scenario is demonstrated. The hash code of some objects in the figure may be different every time the JVM instance is started. This is just an example:

It is also mentioned in the note that the weakhashmap in the holder is used as a set container, and the mapped values are null. Each time you traverse all its keys, you can obtain all transmittablethreadlocal instances in the holder. It is a global memory, but it is an inheritablethreadlocal. The mapping relationship after multi-threaded sharing will be relatively complex:

Let's talk about the role of disableignorenullvaluesemantics. By default, disableignorenullvaluesemantics = false. If the TTL value is set to null, the corresponding TTL instance will be directly removed from the holder. When the ttl#get() method is called, if the originally held property is not null, the TTL instance will be added to the holder again. If disableignorenullvaluesemantics = true, the semantics of set (null) is consistent with ThreadLocal. See the following example:

public class TtlSample4 {

    static TransmittableThreadLocal<Integer> TL1 = new TransmittableThreadLocal<Integer>(false) {
        @Override
        protected Integer initialValue() {
            return 5;
        }

        @Override
        protected Integer childValue(Integer parentValue) {
            return 10;
        }
    };

    static TransmittableThreadLocal<Integer> TL2 = new TransmittableThreadLocal<Integer>(true) {
        @Override
        protected Integer initialValue() {
            return 5;
        }

        @Override
        protected Integer childValue(Integer parentValue) {
            return 10;
        }
    };

    public static void main(String[] args) throws Exception {
        TL1.set(null);
        TL2.set(null);
        Thread t1 = new Thread(TtlRunnable.get(() -> {
            System.out.println(String.format("Thread:%s,value:%s",Thread.currentThread().getName(),TL1.get()));
        }),"T1");

        Thread t2 = new Thread(TtlRunnable.get(() -> {
            System.out.println(String.format("Thread:%s,TL2.get()));
        }),"T2");
        t1.start();
        t2.start();
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }
}
// 输出结果:
Thread:T2,value:null
Thread:T1,value:5

This is because the designer of the framework does not want to take null as a stateful value. If it is really necessary to maintain the same usage as ThreadLocal, you can pass in true when constructing the transmittablethreadlocal instance.

Transmitter

Transmitter transmitter is a public static class of transmittablethreadlocal. Its core function is to transfer all transmittablethreadlocal instances and provide static methods to register the variables of the current thread to other threads. According to the author's habit of reading the source code, first look at the constructor and key attributes:

// # TransmittableThreadLocal#Transmitter
public static class Transmitter {
    
    // 保存手动注册的ThreadLocal->TtlCopier映射,这里是因为部分API提供了TtlCopier给用户实现
    private static volatile WeakHashMap<ThreadLocal<Object>,TtlCopier<Object>> threadLocalHolder = new WeakHashMap<ThreadLocal<Object>,TtlCopier<Object>>();
    // threadLocalHolder更变时候的监视器
    private static final Object threadLocalHolderUpdateLock = new Object();
    // 标记WeakHashMap中的ThreadLocal的对应值为NULL的属性,便于后面清理
    private static final Object threadLocalClearMark = new Object();
    
    // 默认的拷贝器,影子拷贝,直接返回父值
    private static final TtlCopier<Object> shadowCopier = new TtlCopier<Object>() {
        @Override
        public Object copy(Object parentValue) {
            return parentValue;
        }
    };
    
    // 私有构造,说明只能通过静态方法提供外部调用
    private Transmitter() {
        throw new InstantiationError("Must not instantiate this class");
    }
    
    // 私有静态类,快照,保存从holder中捕获的所有TransmittableThreadLocal和外部手动注册保存在threadLocalHolder的ThreadLocal的K-V映射快照
    private static class Snapshot {
        final WeakHashMap<TransmittableThreadLocal<Object>,Object> ttl2Value;
        final WeakHashMap<ThreadLocal<Object>,Object> threadLocal2Value;

        private Snapshot(WeakHashMap<TransmittableThreadLocal<Object>,Object> ttl2Value,WeakHashMap<ThreadLocal<Object>,Object> threadLocal2Value) {
            this.ttl2Value = ttl2Value;
            this.threadLocal2Value = threadLocal2Value;
        }
    }
}

Transmitter is a typical tool class in design. Only its public static methods can be called externally. Then look at other static methods:

// # TransmittableThreadLocal#Transmitter
public static class Transmitter {

    //######################################### 捕获 ###########################################################

    // 捕获当前线程绑定的所有的TransmittableThreadLocal和已经注册的ThreadLocal的值 - 使用了用时拷贝快照的策略
    // 笔者注:它一般在构造任务实例的时候被调用,因此当前线程相对于子线程或者线程池的任务就是父线程,其实本质是捕获父线程的所有线程本地变量的值
    @NonNull
    public static Object capture() {
        return new Snapshot(captureTtlValues(),captureThreadLocalValues());
    }
    
    // 新建一个WeakHashMap,遍历TransmittableThreadLocal#holder中的所有TransmittableThreadLocal的Entry,获取K-V,存放到这个新的WeakHashMap返回
    private static WeakHashMap<TransmittableThreadLocal<Object>,Object> captureTtlValues() {
        WeakHashMap<TransmittableThreadLocal<Object>,Object> ttl2Value = new WeakHashMap<TransmittableThreadLocal<Object>,Object>();
        for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
            ttl2Value.put(threadLocal,threadLocal.copyValue());
        }
        return ttl2Value;
    }
    
    // 新建一个WeakHashMap,遍历threadLocalHolder中的所有ThreadLocal的Entry,获取K-V,存放到这个新的WeakHashMap返回
    private static WeakHashMap<ThreadLocal<Object>,Object> captureThreadLocalValues() {
        final WeakHashMap<ThreadLocal<Object>,Object> threadLocal2Value = new WeakHashMap<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;
    }

    //######################################### 重放 ###########################################################

    // 重放capture()方法中捕获的TransmittableThreadLocal和手动注册的ThreadLocal中的值,本质是重新拷贝holder中的所有变量,生成新的快照
    // 笔者注:重放操作一般会在子线程或者线程池中的线程的任务执行的时候调用,因此此时的holder#get()拿到的是子线程的原来就存在的本地线程变量,重放操作就是把这些子线程原有的本地线程变量备份
    @NonNull
    public static Object replay(@NonNull Object captured) {
        final Snapshot capturedSnapshot = (Snapshot) captured;
        return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value),replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
    }
    
    // 重放所有的TTL的值
    @NonNull
    private static WeakHashMap<TransmittableThreadLocal<Object>,Object> replayTtlValues(@NonNull WeakHashMap<TransmittableThreadLocal<Object>,Object> captured) {
        // 新建一个新的备份WeakHashMap,其实也是一个快照
        WeakHashMap<TransmittableThreadLocal<Object>,Object> backup = new WeakHashMap<TransmittableThreadLocal<Object>,Object>();
        // 这里的循环针对的是子线程,用于获取的是子线程的所有线程本地变量
        for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
            TransmittableThreadLocal<Object> threadLocal = iterator.next();

            // 拷贝holder当前线程(子线程)绑定的所有TransmittableThreadLocal的K-V结构到备份中
            backup.put(threadLocal,threadLocal.get());

            // 清理所有的非捕获快照中的TTL变量,以防有中间过程引入的额外的TTL变量(除了父线程的本地变量)影响了任务执行后的重放操作
            // 简单来说就是:移除所有子线程的不包含在父线程捕获的线程本地变量集合的中所有子线程本地变量和对应的值
            /**
             * 这个问题可以举个简单的例子:
             * static TransmittableThreadLocal<Integer> TTL = new TransmittableThreadLocal<>();
             * 
             * 线程池中的子线程C中原来初始化的时候,在线程C中绑定了TTL的值为10087,C线程是核心线程不会主动销毁。
             * 
             * 父线程P在没有设置TTL值的前提下,调用了线程C去执行任务,那么在C线程的Runnable包装类中通过TTL#get()就会获取到10087,显然是不符合预期的
             *
             * 所以,在C线程的Runnable包装类之前之前,要从C线程的线程本地变量,移除掉不包含在父线程P中的所有线程本地变量,确保Runnable包装类执行期间只能拿到父线程中捕获到的线程本地变量
             *
             * 下面这个判断和移除做的就是这个工作
             */
            if (!captured.containsKey(threadLocal)) {
                iterator.remove();
                threadLocal.superRemove();
            }
        }

        // 重新设置TTL的值到捕获的快照中
        // 其实真实的意图是:把从父线程中捕获的所有线程本地变量重写设置到TTL中,本质上,子线程holder里面的TTL绑定的值会被刷新
        setTtlValuesTo(captured);

        // 回调模板方法beforeExecute
        doExecuteCallback(true);

        return backup;
    }
    
    // 提取WeakHashMap中的KeySet,遍历所有的TransmittableThreadLocal,重新设置VALUE
    private static void setTtlValuesTo(@NonNull WeakHashMap<TransmittableThreadLocal<Object>,Object> ttlValues) {
        for (Map.Entry<TransmittableThreadLocal<Object>,Object> entry : ttlValues.entrySet()) {
            TransmittableThreadLocal<Object> threadLocal = entry.getKey();
            // 重新设置TTL值,本质上,当前线程(子线程)holder里面的TTL绑定的值会被刷新
            threadLocal.set(entry.getValue());
        }
    }
    
    // 重放所有的手动注册的ThreadLocal的值
    private static WeakHashMap<ThreadLocal<Object>,Object> replayThreadLocalValues(@NonNull WeakHashMap<ThreadLocal<Object>,Object> captured) {
        // 新建备份
        final WeakHashMap<ThreadLocal<Object>,Object> backup = new WeakHashMap<ThreadLocal<Object>,Object>();
        // 注意这里是遍历捕获的快照中的ThreadLocal
        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();
            // 如果值为清除标记则绑定在当前线程的变量进行remove,否则设置值覆盖
            if (value == threadLocalClearMark) threadLocal.remove();
            else threadLocal.set(value);
        }
        return backup;
    }

    // 从relay()或者clear()方法中恢复TransmittableThreadLocal和手工注册的ThreadLocal的值对应的备份
    // 笔者注:恢复操作一般会在子线程或者线程池中的线程的任务执行的时候调用
    public static void restore(@NonNull Object backup) {
        final Snapshot backupSnapshot = (Snapshot) backup;
        restoreTtlValues(backupSnapshot.ttl2Value);
        restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
    }

    private static void restoreTtlValues(@NonNull WeakHashMap<TransmittableThreadLocal<Object>,Object> backup) {
        // 回调模板方法afterExecute
        doExecuteCallback(false);
        // 这里的循环针对的是子线程,用于获取的是子线程的所有线程本地变量
        for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
            TransmittableThreadLocal<Object> threadLocal = iterator.next();
            // 如果子线程原来就绑定的线程本地变量的值,如果不包含某个父线程传来的对象,那么就删除
            // 这一步可以结合前面reply操作里面的方法段一起思考,如果不删除的话,就相当于子线程的原来存在的线程本地变量绑定值被父线程对应的值污染了
            if (!backup.containsKey(threadLocal)) {
                iterator.remove();
                threadLocal.superRemove();
            }
        }

        // 重新设置TTL的值到捕获的快照中
        // 其实真实的意图是:把子线程的线程本地变量恢复到reply()的备份(前面的循环已经做了父线程捕获变量的判断),本质上,等于把holder中绑定于子线程本地变量的部分恢复到reply操作之前的状态
        setTtlValuesTo(backup);
    }
    
    // 恢复所有的手动注册的ThreadLocal的值
    private static void restoreThreadLocalValues(@NonNull WeakHashMap<ThreadLocal<Object>,Object> backup) {
        for (Map.Entry<ThreadLocal<Object>,Object> entry : backup.entrySet()) {
            final ThreadLocal<Object> threadLocal = entry.getKey();
            threadLocal.set(entry.getValue());
        }
    }
}   

The three core methods here look more abstract. They can be easily understood only by considering multi-threaded scenarios and some spatial imagination:

SetTtlValuesTo () is a more subtle method, especially in combination with multithreading and spatial thinking. For example, when the input parameter is captured, it is essentially all thread local variables bound to the parent thread captured from the parent thread, and the calling time is in reply () and restore (). These two methods are only invoked in the sub thread. The transmittablethreadlocal instance obtained in setttlvaluesto() calls the set() method, which is equivalent to refreshing the values of all thread local variables bound to the parent thread to the values of thread local variables in the TTL currently bound to the child thread. Think more deeply, It refreshes the value of the thread local variable bound to the child thread in the global memory holder based on the external incoming value.

There are many static tools and methods in the transmitter, which are not expanded here. You can refer to the test demo and readme in the project MD for debugging.

Capture, replay, and recovery

In fact, the above section has introduced the capture, playback and recovery APIs provided by transmitter. This section mainly analyzes the relevant logic in ttlrunnable. The source code of ttlrunnable is as follows:

public final class TtlRunnable implements Runnable,TtlWrapper<Runnable>,TtlEnhanced,TtlAttachments {

    // 存放从父线程捕获得到的线程本地变量映射的备份
    private final AtomicReference<Object> capturedRef;
    // 原始的Runable实例
    private final Runnable runnable;
    // 执行之后是否释放TTL值引用
    private final boolean releaseTtlValueReferenceAfterRun;

    private TtlRunnable(@NonNull Runnable runnable,boolean releaseTtlValueReferenceAfterRun) {
        // 这里关键点:TtlRunnable实例化的时候就已经进行了线程本地变量的捕获,所以一定是针对父线程的,因为此时任务还没提交到线程池
        this.capturedRef = new AtomicReference<Object>(capture());
        this.runnable = runnable;
        this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
    }

    @Override
    public void run() {
        // 获取父线程捕获到的线程本地变量映射的备份,做一些前置判断
        Object captured = capturedRef.get();
        if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured,null)) {
            throw new IllegalStateException("TTL value reference is released after run!");
        }
        // 重放操作
        Object backup = replay(captured);
        try {
            // 真正的Runnable调用
            runnable.run();
        } finally {
            // 复原操作
            restore(backup);
        }
    }

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

    @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);
    }
    
    // 省略其他不太重要的方法
}

In fact, the focus only needs to be on the constructor and run () method, and others are modified or extended based on this. The source code of the constructor shows that capture() has been called when ttlrunnable is instantiated. The parent thread is the one instantiating it. Therefore, the overall execution process is as follows:

Agent module

To enable the agent function, you need to add: - javaagent: path / to / transmittable-thread-local-x.yzx jar。 The principle is to stimulate classfiletransformer through the instrumentation callback to enhance the bytecode of the target class. When javassist is used, the enhanced classes are mainly classes of the universal thread pool:

The entry class of agent is ttlagent. Check the corresponding source code here:

public final class TtlAgent {
    
    public static void premain(String agentArgs,@NonNull Instrumentation inst) {
        kvs = splitCommaColonStringToKV(agentArgs);

        Logger.setLoggerImpltype(getLogImpltypeFromAgentArgs(kvs));
        final Logger logger = Logger.getLogger(TtlAgent.class);

        try {
            logger.info("[TtlAgent.premain] begin,agentArgs: " + agentArgs + ",Instrumentation: " + inst);
            final boolean disableInheritableForThreadPool = isDisableInheritableForThreadPool();
            // 装载所有的JavassistTransformlet
            final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();
            transformletList.add(new TtlExecutorTransformlet(disableInheritableForThreadPool));
            transformletList.add(new TtlForkJoinTransformlet(disableInheritableForThreadPool));
            if (isEnableTimerTask()) transformletList.add(new TtlTimerTaskTransformlet());
            final ClassFileTransformer transformer = new TtlTransformer(transformletList);
            inst.addTransformer(transformer,true);
            logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
            logger.info("[TtlAgent.premain] end");
            ttlAgentLoaded = true;
        } catch (Exception e) {
            String msg = "Fail to load TtlAgent,cause: " + e.toString();
            logger.log(Level.SEVERE,msg,e);
            throw new IllegalStateException(msg,e);
        }
    }
}

List < javassisttransformlet > is passed as a parameter into the implementation class ttltransformer of classfiletransformer. The conversion method is:

public class TtlTransformer implements ClassFileTransformer {

    private final List<JavassistTransformlet> transformletList = new ArrayList<JavassistTransformlet>();

    TtlTransformer(List<? extends JavassistTransformlet> transformletList) {
        for (JavassistTransformlet transformlet : transformletList) {
            this.transformletList.add(transformlet);
            logger.info("[TtlTransformer] add Transformlet " + transformlet.getClass() + " success");
        }
    }

    @Override
    public final byte[] transform(@Nullable final ClassLoader loader,@Nullable final String classFile,final Class<?> classBeingredefined,final ProtectionDomain protectionDomain,@NonNull final byte[] classFileBuffer) {
        try {
            // Lambda has no class file,no need to transform,just return.
            if (classFile == null) return NO_TRANSFORM;
            final String className = toClassName(classFile);
            ClassInfo classInfo = new ClassInfo(className,classFileBuffer,loader);
            // 这里做变量,如果字节码被修改,则跳出循环返回
            for (JavassistTransformlet transformlet : transformletList) {
                transformlet.doTransform(classInfo);
                if (classInfo.isModified()) return classInfo.getCtClass().toBytecode();
            }
        } catch (Throwable t) {
            String msg = "Fail to transform class " + classFile + ",cause: " + t.toString();
            logger.log(Level.SEVERE,t);
            throw new IllegalStateException(msg,t);
        }
        return NO_TRANSFORM;
    }
}

Here, select some methods of ttlexecutortransformlet:

    @Override
    public void doTransform(@NonNull final ClassInfo classInfo) throws IOException,NotFoundException,CannotCompileException {
        // 如果当前加载的类包含java.util.concurrent.ThreadPoolExecutor或者java.util.concurrent.ScheduledThreadPoolExecutor
        if (EXECUTOR_CLASS_NAMES.contains(classInfo.getClassName())) {
            final CtClass clazz = classInfo.getCtClass();
            // 遍历所有的方法进行增强
            for (CtMethod method : clazz.getDeclaredMethods()) {
                updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(method);
            }
            // 省略其他代码
        } 
        // 省略其他代码
    }

    private void updateSubmitMethodsOfExecutorClass_decorateToTtlWrapperAndSetAutoWrapperAttachment(@NonNull final CtMethod method) throws NotFoundException,CannotCompileException {
        final int modifiers = method.getModifiers();
        if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) return;
        // 这里主要在java.lang.Runnable构造时候调用com.alibaba.ttl.TtlRunnable#get()包装为com.alibaba.ttl.TtlRunnable
        // 在java.util.concurrent.Callable构造时候调用com.alibaba.ttl.TtlCallable#get()包装为com.alibaba.ttl.TtlCallable
        // 并且设置附件K-V为ttl.is.auto.wrapper=true
        CtClass[] parameterTypes = method.getParameterTypes();
        StringBuilder insertCode = new StringBuilder();
        for (int i = 0; i < parameterTypes.length; i++) {
            final String paramTypeName = parameterTypes[i].getName();
            if (PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.containsKey(paramTypeName)) {
                String code = String.format(
                        // decorate to TTL wrapper,// and then set AutoWrapper attachment/Tag
                        "$%d = %s.get($%d,true);"
                                + "\ncom.alibaba.ttl.threadpool.agent.internal.transformlet.impl.Utils.setAutoWrapperAttachment($%<d);",i + 1,PARAM_TYPE_NAME_TO_DECORATE_METHOD_CLASS.get(paramTypeName),i + 1);
                logger.info("insert code before method " + signatureOfMethod(method) + " of class " + method.getDeclaringClass().getName() + ": " + code);
                insertCode.append(code);
            }
        }
        if (insertCode.length() > 0) method.insertBefore(insertCode.toString());
    }

The function of the method analyzed above is to make Java util. concurrent. ThreadPoolExecutor and Java util. concurrent. The bytecode of scheduledthreadpoolexecutor is enhanced to submit Java Tasks of lang. runnable type will be wrapped as ttlrunnable and submitted in Java util. concurrent. Callable tasks will be packaged as ttlcallable, which realizes the function of embedding TTL without intrusion and perception.

Summary

When using thread pools and other execution components that pool reusable threads, TTL provides the function of transmitting ThreadLocal value to solve the problem of context transmission during asynchronous execution. It is a Java standard library, which provides the standard configuration capability for the development of framework / Middleware facilities. The project code is concise. It only relies on javassist for bytecode enhancement to realize the characteristics of almost no intrusion and TTL function in agent mode. TTL can transparently / automatically complete the customizable and standardized capture / transfer of all asynchronous execution contexts in business code. If you happen to encounter the problem of context transfer during asynchronous execution, it is recommended to try this library.

reference material:

Personal blog

(c-14-d, e-a-20200502)

The official account of Technology (Throwable Digest), which is not regularly pushed to the original technical article (never copied or copied):

Entertainment official account ("sand sculpture"), select interesting sand sculptures, videos and videos, push them to relieve life and work stress.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>