Most families with multiple children have to buy more than one toy. If just buy one, hey hey is more exciting. This is avoiding sharing, giving each child a toy corresponds to us in Java, where each thread has its own local variable, we play with our own, avoid fighting, harmony makes thread safe.
Java implements thread-local storage through ThreadLocal.
This idea is very clear, is that each Thread should have its own local variables bai, then the Thread on the inside to get a private property bai ThreadLocal. ThreadLocalMap threadLocals = null; This is the relationship shown below
ThreadLocal
The simple application is as follows
public class Demo {
private static final ThreadLocal<Foo> fooLocal = new ThreadLocal<Foo>();
public static Foo getFoo() {
return fooLocal.get();
}
public static void setFoo(Foo foo) { fooLocal.set(foo); }}Copy the code
Taking a closer look at the internals, ThreadLocalMap is an internal static class of ThreadLocal. It is called a Map but has no relation to java.util.map, except that it implements functions like a Map
static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<? >> { Object value; Entry(ThreadLocal<? > k, Object v) { super(k); value = v; } } private static final int INITIAL_CAPACITY = 16; private Entry[] table;Copy the code
As you can see, ThreadLocalMap contains an array of entries. Only arrays do not have lists like a HashMap does, so ThreadLocalMap uses linear probing to resolve hash collisions.
Linear detection is to determine the position of elements in the table array according to the hashcode value of the initial key. If elements with other key values have been occupied in this position, a fixed algorithm is used to find the next position of a certain step length until the position that can be stored is found. The step size in ThreadLocalMap is 1.
Resolving hash conflicts this way is inefficient, so be aware of the number of ThreadLocal’s.
/**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
Copy the code
And you can see that this Entry has a weak reference to ThreadLocal as the key. So why make weak references (weak references are recycled whenever GC happens)?
First, ThreadLocal does not store any values internally, it is only used as the key of our ThreadLocalMap so that the thread can retrieve the corresponding value. So when we don’t need this key, we’re going to make fooLocal=null so strong references are gone. If Entry is also a strong reference, then this ThreadLocal instance has a strong reference, and we want GC to recycle fooLocal. Then maybe some people think, you make weak reference is not very dangerous ah, in case the GC is not gone? Don’t be afraid if fooLocal is a strong reference in this ThreadLocal instance it won’t be reclaimed. (For strong and weak virtual citation, you can see the difference between four kinds of citation in my previous article.)
Weak references, therefore, mainly allow useless ThreadLocal to be scavenged by GC.
If the key is removed, what about the value? The Entry is still there. Yes, in the case of thread pools, the persistence of a value after the key of some large object has been removed can cause a memory leak due to the thread lifetime.
But Java takes this into account. The get() and set() methods are called to find the entry whose key was killed and then kill it. The remove() method is also provided. Although get() and set() will clear the Entry with a null key, this will not be done every time the call is made. This will be done only when the direct hash is not in get, or when the direct hash is not in set, and when the linear probe starts, the Entry with a null key will be cleared.
Private Entry getEntry(ThreadLocal<? > key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i];if(e ! = null && e.get() == key)returne; // If hit, return directlyelse
returngetEntryAfterMiss(key, i, e); } private Entry getEntryAfterMiss(ThreadLocal<? > key, int i, Entry e) { Entry[] tab = table; int len = tab.length;while(e ! = null) {// start probing ThreadLocal<? > k = e.get();if(k == key) // Return on hitreturn e;
if(k == null) // expungeStaleEntry(I);elsei = nextIndex(i, len); E = TAB [I]; }returnnull; } / /setMethods private voidset(ThreadLocal<? > key, Object value) { Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1);for(Entry e = tab[i]; e ! = null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();if(k == key) { e.value = value; // Replace the original value if it already existsreturn;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if(! cleanSomeSlots(i, sz) && sz >= threshold)rehash(a); }Copy the code
Therefore, it is better to call the remove() method when threadLocal is not needed.
conclusion
Thread local storage is designed to avoid sharing, so pay attention to memory leaks and hash collisions. Threadlocal is widely used for transactions in Spring.
If there are mistakes welcome to correct!
Personal public account: Yes training level guide
There are related interview advanced materials (distributed, performance tuning, classic books PDF) waiting to be collected