Java source code analysis and interview questions-Scene combat: ThreadLocal practice in the context of value-passing scenarios

This series of related blog, Mu class reference column Java source code and system manufacturers interviewer succinctly Zhenti
below this column is GitHub address:
Source resolved: https://github.com/luanqiu/java8
article Demo: HTTPS: // GitHub. com / luanqiu / java8_demo
classmates can look at it if necessary)

Java source code analysis and interview questions-Scene combat: ThreadLocal practice in the context of value-passing scenarios

In the opening remarks,
we have simply improved the process engine in the article "Impressing the interviewer: the actual use of thread pool process orchestration". This article continues to transform on the basis of it. It is recommended that students take a look at the code on GitHub, or Check out the previous article.

1 Review

The object orchestrated by the process engine is called a component (that is, SpringBean). Before we defined a common interface for the component, the interface was implemented when the component was implemented. The code is as follows:
Insert picture description here
We defined the DomainAbilityBean interface, and the input and output parameters are FlowContent , FlowContent we call the context.

2 ThreadLocal implementation

In addition to the implementation of FlowContent, ThreadLocal is also achievable. Let's demonstrate:

2.1 Define ThreadLocal context tool class

First, we use ThreadLocal to define the context tool class, and define put and get methods for easy use. The code is as follows:

public class ContextCache implements Serializable {
 
  private static final long serialVersionUID = 2136539028591849277L;
 
  // 使用 ThreadLocal 缓存上下文信息
  public static final ThreadLocal<Map<String,String>> CACHE = new ThreadLocal<>();
 
  /**
   * 放数据
   * @param sourceKey
   */
  public static final void putAttribute(String sourceKey,String value){
    Map<String,String> cacheMap = CACHE.get();
    if(null == cacheMap){
      cacheMap = new HashMap<>();
    }
    cacheMap.put(sourceKey,value);
    CACHE.set(cacheMap);
  }
 
  /**
   * 拿数据
   * @param sourceKey
   */
  public static final String getAttribute(String sourceKey){
    Map<String,String> cacheMap = CACHE.get();
    if(null == cacheMap){
      return null;
    }
    return cacheMap.get(sourceKey);
  }
 
}

If you want to put data to ThreadLocal, call ContextCache.putAttribute method, if you want to get data from ThreadLocal, call ContextCache.getAttribute method.

We wrote two components, one component puts the data, and one component takes the data, as follows:
Insert picture description here
we register two SpringBeans in the process registration center and let them execute in the order of first executing BeanThree and then BeanFive, and running the main DemoApplication class The method is executed, and the results are as follows:
Insert picture description here
From the printed log, you can see that in the SpringBean managed by the Spring container, ThreadLocal can also store intermediate cache values.

3 Start child thread

Let's do an experiment. We start the child thread in BeanFive, and then get the value from ThreadLocal to see if we can get the value. The code of BeanFive is modified as follows:
Insert picture description here
Let's run it again, and the printed log is as follows:
Insert picture description here
from the printed log In, we found that when the value of ThreadLocal in the child thread is not obtained, the reason is mainly that we said before, when the thread is created, it will not copy the value in the parent thread's ThreadLocal to the child thread. ThreadLocal, the solution is to modify ThreadLocal to InheritableThreadLocal, the code is modified as follows:
Insert picture description here
we run again, the results are as follows:
Insert picture description here
From the running results, we successfully get the value in the child thread.

4 Thread pool + ThreadLocal

If the springBean that took the data was thrown to the thread pool for execution, can we successfully get the data from the ThreadLocal?

First of all, in the springBean that puts the data, modify the value to be random, and then modify the SpringBean that takes the data to execute asynchronously. The code is modified as follows:
Insert picture description here
In order to see the effect quickly, we modify all the coreSize and maxSize of the thread pool to 3, and let the task sleep for a period of time, so that the three threads will definitely not consume the task, a large number of tasks will be queued in the queue, we modify the test script, as follows:
Insert picture description here
we expect the result:

  1. BeanFive executed in the thread pool can successfully get data from ThreadLocal;
  2. Can get the correct data from ThreadLocal, such as BeanThree just put in key1, value5, then expect to get value5 according to key1 in BeanFive instead of other values.

Let's run it and the results are as follows:
Insert picture description here
From the results, we can see that it did not meet our expectations. We put many values ​​into the ThreadLocal, but many of the values ​​that came out were value379, all of which were put into the ThreadLocal. value.

The main reason for this is that the references to the HashMap stored in ThreadLocal are the same. The main thread can modify the values ​​in the HashMap. When the child thread takes the value from the ThreadLocal, it also takes the value from the HashMap, which results in the failure to pass the value of put through the ThreadLocal. Correctly passed to the child thread.

In order to prove this reason, we print the memory address of HashMap in the place where we put and get the value from ThreadLocal, and change the code as follows:
Insert picture description here
we run the test code again, and the results are as follows:
Insert picture description here
from the test results, we can see When the main thread or the sub-thread interacts with the ThreadLocal, the HashMap is the same, which means that the HashMap saved in the ThreadLocal is shared, which leads to the problem of thread safety, and the value read by the sub-thread will be confused .

5 Solution

In response to this problem, we have proposed a solution. When submitting the task to the thread pool, we make a copy of the HashMap, so that the HashMap of the child thread and the HashMap of the main thread are different, which can solve the above problem.

When we submit the task, we use Runnable. To realize the copy of HashMap, we need to wrap Runnable in one layer. The packaging code is as follows: The
Insert picture description here
running result is as follows:
Insert picture description here
from the running result, we can see that the value from the thread pool has been Is correct.

6 Summary

This article uses ThreadLocal to transform the context transmission in the process engine, hoping to deepen everyone's understanding and use skills of ThreadLocal. Interested students can download our code and run around.

Published 40 original articles · won praise 1 · views 5355

Guess you like

Origin blog.csdn.net/aha_jasper/article/details/105609455