ThreadLocal is a thread-private local variable storage container. It can be understood that each thread has its own storage container for storing thread-private variables. ThreadLocal is widely used in daily development frameworks, but if it is not used properly, various problems will arise. This article will explain it.
1. Application scenarios
There are two common application scenarios for ThreadLocal:
- In multi-threaded concurrent scenarios, it is used to ensure thread safety.
- When dealing with more complex business, use ThreadLocal instead of parameter display transmission.
1.1. Ensure thread safety
When multiple threads access the same shared variable, concurrency problems are prone to occur, especially when multiple threads write to a variable. In order to ensure thread safety, general users need to perform additional synchronization measures when accessing shared variables. Thread safety, such as: synchronized, Lock and other locks.
ThreadLocal is a method other than the synchronization method of locking, which avoids the thread-unsafe method of multi-threaded access. When we create a variable, if each thread accesses it is the thread's own variable, there will be no thread insecurity problem.
ThreadLocal is provided by the JDK package. It provides thread local variables. If a ThreadLocal variable is created, each thread that accesses this variable will have a copy of the variable. In actual multi-threaded operations, it operates in its own local memory. variable, thus avoiding thread safety issues.
1.2. Display passed parameters
Here are a few examples:
Example 1: Get the current requesting user of the interface
In the whole process of the background interface business logic, if you need to obtain the information of the current requesting user in multiple places. A common approach is to obtain the current user information from the session or token through filters, interceptors, AOP, etc., and store it in ThreadLocal when requesting an interface.
During the entire interface processing process, if no additional threads are created, the current user can be obtained directly from the ThreadLocal variable, without the need to verify and obtain the user from the Session and token. This scheme design not only improves performance, but also makes the original complex logic and code implementation concise and clear. For example the following example:
(1) Define the ThreadLocal variable: UserProfileThread.java
public class UserProfileThread {
private static ThreadLocal<UserProfile> USER_PROFILE_TL =new ThreadLocal<>();
public static void setUserProfile(UserProfile userProfile){
USER_PROFILE_TL.set(userProfile);
}
public static UserProfile getUserProfile() {
return USER_PROFILE_TL.get();
}
public static String getCurrentUser() {
return Optional.ofNullable(USER_PROFILE_TL.get())
.map(UserProfile::getUid)
.orElse(UserProfile.ANONYMOUS_USER);
}
}
(2) Set the variable value in the filter:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
UserProfile userProfile = null;
// ... 验证和获取用户信息 userProfile
UserProfileThread.setUserProfile(userProfile);
filterChain.doFilter(servletRequest, servletResponse);
}
(3) Get current user information
//获取当前用户
String uid=UserProfileThread.getCurrentUser();
//获取当前用户对象
UserProfile user=UserProfileThread.getUserProfile();
Example 2: Ensure that database transactions are executed under the same connection in the spring framework
To implement jdbc transactions, it must be operated in the same connection object, and transactions under multiple connections will be uncontrollable and need to be completed with the help of distributed transactions. How does the spring framework ensure that database transactions are executed under the same connection?
DataSourceTransactionManager is spring's data source transaction manager. It will get a connection from the database connection pool when you call getConnection(), then bind it to ThreadLocal, and unbind it after the transaction is completed. This ensures that the transaction is completed under the same connection.
2. Implementation principle
The ThreadLocal class provides set/get methods to store and obtain value values, but in fact, the ThreadLocal class does not store value values. The real storage relies on the ThreadLocalMap class.
Each thread instance corresponds to a TheadLocalMap instance. We can instantiate many ThreadLocals in the same thread to store many types of values. These ThreadLocal instances are used as keys, corresponding to their respective values, and finally stored in the Entry table array.
Let's look at the set method of ThreadLocal:
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// 省略其他方法
}
The logic of set is relatively simple, that is, to obtain the ThreadLocalMap of the current thread, and then add KV to the map, where K is the current ThreadLocal instance, and V is the value we passed in. It should be noted here that the acquisition of map needs to be taken from the Thread class object. Take a look at the definition of the Thread class.
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
//省略其他
}
The Thread class maintains a ThreadLocalMap of variable references.
Therefore, we can draw the following conclusions:
- Each thread is a Thread instance, which internally maintains an instance member of threadLocals whose type is ThreadLocal.ThreadLocalMap.
- ThreadLocal itself is not a container. The value we access is actually stored in ThreadLocalMap, and ThreadLocal is only used as the key of TheadLocalMap.
3. Recommend static modification
It is recommended that ThreadLocal be modified with static.
First of all, static modified variables are allocated addresses when the class is loaded, and will be recycled when the class is unloaded.
The principle of ThreadLocal is that there is a collection object of ThreadLocalMap inside Thread. Its key is ThreadLocal, and its value is the copy of the variable you want to store. Different threads have their ThreadLocalMap isolated. If the variable ThreadLocal is non-static, it will cause every Different ThreadLocal objects must be generated each time an instance is generated. Although the program will not have any exceptions, it will waste memory resources and cause memory leaks.
A ThreadLocal instance corresponds to a TSO (thread specific object, that is, a thread-related variable) instance in the current thread. Therefore, if ThreadLocal is declared as an instance variable of a class (rather than a static variable), each creation of an instance of that class will result in a new TSO instance being created. Obviously, these TSO instances being created are instances of the same class. As a result, the same thread may access different instances of the same TSO (referring to the class), which can lead to waste (repeated creation of identical objects), if not an error! Therefore, generally we can use static modification of ThreadLocal.
4. Precautions
ThreadLocal instances provide a remove() method to recycle objects and clear the corresponding memory usage. This method is often easily overlooked, leading to various problems. Such as the following:
- Thread reuse: In the example of "Get the current requesting user of the interface", Tomcat processes user requests through the thread pool, and the threads in the thread pool are reused. There will definitely be a situation where a thread is reused by the interface requests of different users before and after, so it is necessary to overwrite or clear the used ThreaLocal variable.
- Memory overflow: Since the life cycle of ThreadLocalMap is as long as Thread, if there are many ThreadLocal variables created, that is, the corresponding key occupies a large amount of memory, but it is not manually deleted, which will lead to memory leaks to a certain extent.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。