This time I will introduce to you the important tool ThreadLocal. The explanation is as follows. It also introduces the scenarios in which memory leaks occur, how to reproduce memory leaks, and how to use it correctly to avoid memory leaks.
- What is ThreadLocal? What are its uses?
- How to use ThreadLocal
- ThreadLocal principle
- What are the pitfalls and precautions when using ThreadLocal?
1. What is ThreadLocal? What are its uses?
First introduce the attribute threadLocals in the Thread class:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
We found that Thread does not provide a method for setting and accessing the member variable threadLocals. So how do we operate the instance threadLocals parameters of each thread? At this time our protagonist: ThreadLocal appears.
So there is a summary: ThreadLocal is the manager of the threadLocals attribute in thread Thread . In other words, the results of our ThreadLocal get, set, and remove operations are all threadLocals storage, access, and deletion operations for the current thread Thread instance. Similar to a developer's tasks, the product manager cannot control them. The product manager can only assign tasks to developers through the technical leader. Here is another example to further illustrate the relationship between them: that is to say, the results of our ThreadLocal get, set, and remove operations are all threadLocals storage, access, and deletion operations for the current thread Thread instance. Similar to a developer's tasks, the product manager cannot control them. The product manager can only assign tasks to developers through the technical leader. Here is another example to further illustrate the relationship between them:
- Everyone has a bank card
- Everyone has a certain balance on each card.
- Everyone must go through the bank's management system to obtain their bank card balance.
- Everyone can only obtain the balance information held by their own card, and others cannot access it.
Map to the ThreadLocal we want to say
- card is similar to Thread
- Card balance attributes, card number attributes, etc. are similar to Treadlocal’s internal attribute collection threadLocals
- cardManager is similar to the ThreadLocal management class
So what are the application scenarios of ThreadLocal?
In fact, we have inadvertently used the convenience provided by ThreadLocal all the time. If switching between multiple data sources is unfamiliar to you, then the declarative transactions provided by spring are all too familiar. We use it all the time during the research and development process, and spring The important implementation basis of declarative transactions is ThreadLocal, but no one has delved into the implementation mechanism of spring declarative transactions. When I have the opportunity later, I will introduce to you the principles and implementation mechanism of spring declarative transactions.
It turns out that ThreadLocal is so powerful, but it is rarely used by application developers. At the same time, some developers are afraid of trying ThreadLocal due to memory leaks and other potential problems. I am afraid this is the biggest misunderstanding about ThreadLocal. We will analyze it carefully later. As long as it is used correctly, method, there is no problem. If there is a problem with ThreadLocal, isn't spring's declarative transaction the biggest potential danger to our program?
2.How to use ThreadLocal
For a more intuitive experience,
ThreadLocal
we assume the following scenario
- We generate an ID for each thread.
- Once set, it cannot be changed during the thread life cycle.
- Duplicate IDs cannot be generated during container activity
We create a ThreadLocal management class:
The test program is as follows: we use the same thread to get continuously, test whether the id changes, and release it after the test is completed.
In the main program, we start multiple threads to test whether there will be any influence between blocked threads.
Not surprisingly our results are:
Result: It is true that the IDs are different between different threads, but the IDs of the same thread are the same.
3.ThreadLocal principle
①ThreadLocal class structure and method analysis:
As can be seen from the above figure: ThreadLocal
three methods get, set, remove and internal class `ThreadLocalMap
②The relationship between ThreadLocal and Thread:
From this picture, we can intuitively see the attribute threadLocals in Thread. As a special Map, its key value is our ThreadLocal
instance, and the value value is the value we set.
③ThreadLocal operation process:
Let's take the get method as an example:
Among them, getMap(t) returns the threadlocals of the current thread, as shown below, and then uses the current ThreadLocal instance object as the key to obtain the value in the ThreadLocalMap. If it is the first time to come in, call setInitialValue()
The set process is also similar:
Note: ThreadLocal
This can t.threadLocals
be done directly because Thread
under ThreadLocal
the same package, Thread can also be directly accessed ThreadLocal.ThreadLocalMap threadLocals = null;
to declare properties.
4. What are the pitfalls and precautions when using ThreadLocal?
I often see horrifying headlines on the Internet that ThreadLocal causes memory leaks. This usually makes some developers who do not have a thorough understanding of ThreadLocal at the beginning dare not use it rashly. The less used it is, the stranger it becomes. This makes us miss out on better implementation solutions, so only by daring to introduce new technologies and stepping into pitfalls can we continue to make progress.
Let’s take a look at why ThreadLocal can cause memory leaks. In what scenarios can it cause memory leaks?
Let’s first review what a memory leak is, and what the corresponding memory overflow is:
① Memory overflow: Memory overflow, there is not enough memory for the applicant to use.
②Memory leak: Memory leak. After the program applies for memory, it cannot release the applied memory space. The accumulation of memory leaks will eventually lead to memory overflow.
Apparently the memory was not released due to irregular use of TreadLocal.
In the red box we see a special class WeakReference. This class is also rarely used by application developers. Let’s briefly introduce it here.
Since WeakReference is about to be recycled in the next gc, why does our program not have any problems?
①So we test the recycling mechanism of weak references:
This kind of strong reference will not be recycled.
No strong references here will be recycled.
The above demonstrates the recycling of weak references. Let's take a look at the recycling of weak references in ThreadLocal.
②Weak ThreadLocal
reference recycling situation
As shown in the figure above, we have no external strong reference to the ThreadLocal object as the key. The next gc will definitely generate data with a key value of null. If the thread does not end in time, a strong reference chain Threadref–>Thread–>ThreadLocalMap– will inevitably occur
. >Entry, so this will cause a memory leak.
Below we simulate and reproduce the memory leak caused by ThreadLocal:
1. For better effect, we set the storage value of our treadlocals to a list of 10,000 strings:
class ThreadLocalMemory {
// Thread local variable containing each thread's ID
public ThreadLocal<List<Object>> threadId = new ThreadLocal<List<Object>>() {
@Override
protected List<Object> initialValue() {
List<Object> list = new ArrayList<Object>();
for (int i = 0; i < 10000; i++) {
list.add(String.valueOf(i));
}
return list;
}
};
// Returns the current thread's unique ID, assigning it if necessary
public List<Object> get() {
return threadId.get();
}
// remove currentid
public void remove() {
threadId.remove();
}
}
The test code is as follows:
public static void main(String[] args)
throws InterruptedException {
// 为了复现key被回收的场景,我们使用临时变量
ThreadLocalMemory memeory = new ThreadLocalMemory();
// 调用
incrementSameThreadId(memeory);
System.out.println("GC前:key:" + memeory.threadId);
System.out.println("GC前:value-size:" + refelectThreadLocals(Thread.currentThread()));
// 设置为null,调用gc并不一定触发垃圾回收,但是可以通过java提供的一些工具进行手工触发gc回收。
memeory.threadId = null;
System.gc();
System.out.println("GC后:key:" + memeory.threadId);
System.out.println("GC后:value-size:" + refelectThreadLocals(Thread.currentThread()));
// 模拟线程一直运行
while (true) {
}
}
How do we know that there is a memory leak in the memory at this time?
We can use some commands provided by jdk to dump the current heap memory. The commands are as follows:jmap -dump:live,format=b,file=heap.bin <pid>
Then we use the MAT visual analysis tool to view the memory and analyze the survival status of the object instance:
First open our tool to prompt our memory leak analysis:
What we can determine here is that the Entry.value of the ThreadLocalMap instance has not been recycled.
Finally, we need to determine whether Entry.key is still there? Open the Dominator Tree, search for our ThreadLocalMemory, and find that there is no surviving instance.
Above we have reproduced the memory leak caused by improper use of ThreadLocal. The demo is here.
So we summarized the prerequisites for memory leaks when using ThreadLocal:
① The ThreadLocal reference is set to null, and there are no set, get, or remove operations later.
②The thread keeps running without stopping. (Thread pool)
③Garbage collection is triggered. (Minor GC or Full GC)
We see that the conditions for memory leaks in ThreadLocal are still very harsh, so we only need to destroy one of the conditions to avoid memory leaks, but in order to better avoid this situation from happening, we follow the rules when using ThreadLocal The following two small principles:
①ThreadLocal is declared as private static final.
Private and final try to prevent others from modifying the change reference.
Static is expressed as a class attribute and will only be recycled when the program ends.
②Be sure to call the remove method after using ThreadLocal.
The simplest and most effective way is to remove it after use.