Today, let’s talk about ThreadLocal.
❝
Article has been synchronized to making, there is need to have: https://github.com/StackInk/MakerStack.git
❞
1. What is it?
A thread-bound variable class provided in JDK1.2.
“His idea is to clone each thread that uses the resource, so that different threads can use different resources independently of each other“
2. Why?
“Think of a scenario” : When connecting to a database, we create a Connection that can be used by different threads. This is where multiple threads compete for the same resource.
Multiple threads competing for the same resource are common, and there are only two common solutions: “space for time, time for space.”
You can’t have your cake and eat it. Just like our CAP theory, we sacrifice one of them to ensure the other two.
Our solution to the above scenario is as follows:
- Space for time: Create a connection for each thread.
- Create a connection directly in the thread. (” Too much duplicate code “)
- use
ThreadLocal
Bind one connection for each thread.
- Time for space: Locks the current resource so that only one thread can use the connection at a time.
Using ThreadLocal to bind a variable of the specified type to each thread is equivalent to privatizing the thread
3. How does it work?
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
threadLocal.get();
threadLocal.set(1);
threadLocal.remove();
Copy the code
Yes, these four lines of code make it pretty clear how ThreadLocal is used.
get
fromThreadLocal
Pull out an object owned by the current threadset
Binds an object to the current threadremove
Removes the current object bound by the current thread
“Remember, after use, remove, remove, remove“
Why remove? Many of you have heard that ThreadLocal can cause memory leaks.
Yeah, so to deal with that, “So you know, just remove it, don’t waste space.”
Seeing this, there are a lot of question marks in my head (” Do you have a lot of question marks? )
“Why do memory leaks occur?“
“Why not remove the memory leak“
“What does it say about object and thread binding“
“Why does get get the current thread and not the other thread“
“How does it work??“
Come on, “open look, source code”
4. Source code interpretation
An idea: “What if we wrote our own ThreadLocal?”
A thread binds an object. Isn’t this what we know as map mapping? So with a Map we can add a set of threads as keys, objects as values, and then get,set,remove, whatever we want to do, and we’re done. “😀”
“At this time, a brother said. This thread can only store one type of variable, so I want to store multiple variables?“
Feel about your hair volume and you say the words of wisdom: “All problems are rooted in the source and the result.”
Let developers make their own thread private as a result.
Come on, look at the source. Now our requirement is: “Threads can bind multiple values, not just one.” Yeah, that’s right. Say what you think, guys.
“Let the thread maintain a Map by itselfThreadLocal
As aKey
, object asValue
We’ll be done“
“Dude, all hell breaks loose“
At this time, another brother said. If you throw a ThreadLocal into a thread’s Map, that ThreadLocal is “always referenced by the thread object, so it is reachable until the thread is destroyed.”
Ok, question. Think of it this way, since the thread and ThreadLocal object have a reference and cannot GC, I can make the reference between you and the thread weak or soft. One GC and you’re gone.
“What, you don’t know what weak and soft references are??“
I’m going to do the math and I’m going to give you a review.
There are four types of references in the JDK, the default being strong references, which is what we do all the time. Crazy new, new, new. Objects created at this point are strong references.
- Strong reference. directly
new
- Soft reference. through
SoftReference
It is created and destroyed when it runs out of memory, i.e. the last place it will be destroyed is in the old age area - A weak reference. through
WeakReference
Create,GC
When destroyed directly. That is, the destruction site must be Eden - Phantom reference. through
PhantomReference
Create, it’s the same as not saving,“It is very virtual and can only do some operations by referring to queues, mainly used for out-of-heap memory reclamation“
Ok, back to the point, the quote above that best fits our current situation is the weak quote, “Why say it this way:”
In the past, we waited for GC to clean up the object after we used it, but with ThreadLocal, even if we used the end, the thread itself had a reference to the object and was in a reachable state that the garbage collector could not collect. Memory leaks can occur when there are too many ThreadLocal’s.
This problem is solved by treating a reference to a ThreadLocal object as weak. When we run out of ThreadLocal, “the GC will kill the strong references we created, and we can kill the references in the thread Map, so we use weak references, and you can see why we don’t use soft references.”
“One more question: Why does it cause a memory leak?“
Those of you who know the Map structure should know that the interior is actually an array of nodes, and in the case of ThreadLocalMap, the interior is an Entity that treats Key as a weak reference and Value as a strong reference. If we do not remove the Entity after using ThreadLocal, we can cause a memory leak.
ThreadLocalMap provides a method expungeStaleEntry that excludes invalid entities (entities with empty keys).
“There is a question that I have been thinking about for a long time. Why not make the value a weak reference and throw it away when it is used“
Finally think out the answer (according to the source code push) :
“It is not set to weak reference because it is not clearValue
In addition tomap
If no other references exist, whenGC
“, will directly kill the Value, and at this time ourThreadLocal
While in use, Value is null, so make it a strong reference.“
In order to solve the problem of strong references, it provides a mechanism to remove the Entity whose Key is Null
“At this point, the design of this class is clear. Let’s take a look at the source code!“
One point to note: “ThreadLocalMap resolves hash collisions by linear probing.”
“In human terms, if the current array bit has a value, then the next array bit has a value. If there is a value, continue looking until an empty array bit“
“Set method“
class ThreadLocal
public void set(T value) {
// Get the current thread
Thread t = Thread.currentThread();
// Get the ThreadLocalMap of the current thread
ThreadLocalMap map = getMap(t); if(map ! =null) // If a Map has been created for the current thread, set it directly map.set(this, value); else // If no Map is created, the Map is created createMap(t, value); } private void set(ThreadLocal
key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); // Get the current bit of the array, whether the current bit of the array is null, if it is null, directly assign, if it is not null, linear find a NULL, assign for (Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; // Clear some invalid entities if(! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } ThreadLocalMap getMap(Thread t) { // Get the ThreadLocalMap of the current thread return t.threadLocals; } void createMap(Thread t, T firstValue) { // The current object is the Key, as we imagined t.threadLocals = new ThreadLocalMap(this, firstValue); } Copy the code
“The Get method“
public T get(a) {
// Get the current thread
Thread t = Thread.currentThread();
// Get the Map of the current thread
ThreadLocalMap map = getMap(t);
if(map ! =null) { // Get this entity ThreadLocalMap.Entry e = map.getEntry(this); if(e ! =null) { @SuppressWarnings("unchecked") T result = (T)e.value; / / return return result; } } return setInitialValue(); } private Entry getEntry(ThreadLocal
key) { // Compute the array bit int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; // If the current array has a value and the array bits have the same key, value is returned if(e ! =null && e.get() == key) return e; else // Linear probes find corresponding keys return getEntryAfterMiss(key, i, e); } private Entry getEntryAfterMiss(ThreadLocal<? > key,int i, Entry e) { Entry[] tab = table; int len = tab.length; while(e ! =null) { ThreadLocal<? > k = e.get(); if (k == key) return e; if (k == null) // Exclude the currently empty Entity expungeStaleEntry(i); else // Get the next bit i = nextIndex(i, len); e = tab[i]; } // Return null if not found return null; } Copy the code
“remove“
public void remove(a) {
ThreadLocalMap m = getMap(Thread.currentThread());
if(m ! =null)
m.remove(this);
}
private void remove(ThreadLocal
key) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); // Get the current array, check whether it is the desired array bit, if not linear search for (Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); // Empties entities with bit NUll expungeStaleEntry(i); return; } } } Copy the code
“We can see a phenomenon: inset
.get
.remove
Is calledexpungeStaleEntry
To remove all the failuresEntity
remove“
So what does this method do
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// Delete the Value of the entity
tab[staleSlot].value = null; // Empty the array bit tab[staleSlot] = null; // Quantity minus one size--; // Recalculate the hash once, if the current array bit is not NULL, linear search until a NULL Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) ! =null; i = nextIndex(i, len)) { ThreadLocal<? > k = e.get(); if (k == null) { e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if(h ! = i) { tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until // null because multiple entries could have been stale. while(tab[h] ! =null) h = nextIndex(h, len); tab[h] = e; } } } return i; } Copy the code
❝
There are a lot of bad articles, and I am busy recently, which makes the efficiency of writing articles very low. It may be my personal time management problem. I will make up the poor articles as soon as possible.
❞
Refer to blog post:
https://www.cnblogs.com/micrari/p/6790229.html
Previous recommendations:
7, 000 words take you deep into IOC startup principles
MVC Startup Principle
This article is formatted using MDNICE