How to pass Thread context variables when the thread pool is asynchronous

question:

How does tracid pass the id when it encounters thread pool asynchrony?

openFeign asynchronous request loses the above problem?

In the final analysis, these problems are caused by ThreadLocal. Since ThreadLocal can only save the information of the current thread, it cannot realize the inheritance of parent-child threads.

InheritableThreadLocal

Many people think of InheritableThreadLocal. It is true that InheritableThreadLocal can realize the transfer of local variables between parent and child threads, but it may not be able to realize the transfer between parent and child threads when thread reuse is involved, because the existence of threads in the thread pool is not always used every time. To create, InheritableThreadlocal is copied and passed only when intertableThreadLocals=true when the thread is initialized.

Therefore, if the child thread used this time is a thread that has been pooled, it is taken out of the thread pool for offline use without initialization, and the local variables of the parent and child threads will not be copied.

Examples are as follows:

@Test
public void test() throws Exception {
    //single thread pool
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    //InheritableThreadLocal storage
    InheritableThreadLocal<String> username = new InheritableThreadLocal<>();
    for (int i = 0; i < 10; i++) {
    username.set("count—"+i);
    Thread.sleep(3000);
    CompletableFuture.runAsync(()-> System.out.println(username.get()),executorService);
   }
}

In the above code, a single thread pool is created, which is called asynchronously in a loop, and the username is printed. Since the number of core threads is 1, there must be multiplexing of threads.

The print information is as follows:
count—0
count—0
count—0
count—0
count—0
count—0
count—0
count—0
count—0
count—0

see it? The variable transfer between parent and child threads is not implemented here, which is the limitation of InheritableThreadLocal.

TransmittableThreadLocal

TransmittableThreadLocal(TTL): In the case of using a thread pool and other execution components that pool multiplexed threads, it provides the function of passing ThreadLocal values ​​to solve the problem of context transfer during asynchronous execution.

The core functions of the entire TransmittableThreadLocal library (user API and framework/middleware integration API, thread pool ExecutorService/ForkJoinPool/TimerTask and its thread factory Wrapper).

Demand scenario:

Distributed tracking system or full link pressure test (ie link marking)

Log collection records system context

Official website address

add dependencies

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
</dependency

Modify the above code

@Test
public void test() throws Exception {
    //single thread pool
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    //Need to use TtlExecutors to wrap the thread pool
    executorService=TtlExecutors.getTtlExecutorService(executorService);
    //Create TransmittableThreadLocal
    TransmittableThreadLocal<String> username = new TransmittableThreadLocal<>();
    for (int i = 0; i < 10; i++) {
    username.set("count—"+i);
    Thread.sleep(3000);
    CompletableFuture.runAsync(()-> System.out.println(username.get()),executorService);
  }
}

It should be noted that TtlExecutors need to be used to wrap the thread pool, the code is as follows:

executorService=TtlExecutors.getTtlExecutorService(executorService);

The result is as follows:

count—0
count—1
count—2
count—3
count—4
count—5
count—6
count—7
count—8
count—9

It can be seen that the data transfer between parent and child threads in the thread pool has been realized.

Every time the task is called, the TTL data of the current main thread will be copied to the child thread, and then cleared after the execution is completed. At the same time, the modification in the sub-thread does not actually take effect when it returns to the main thread. This ensures that each task is executed without interfering with each other.

The basic principles are as follows:
https://blog.csdn.net/qq_34162294/article/details/125117991

Tags: Java

Posted by musclehead on Tue, 06 Dec 2022 14:51:42 +0530