This is the 12th day of my participation in the Gwen Challenge.More article challenges
Set a Flag write something every day and stick to it.
Understand and use ThreadLocal
Threadlocal is an internal storage class that can store data in a specified thread. Once the data is stored, only the specified thread can get the stored data.
ThreadLocal
/ / set methods
public void set(T value) {
// Get the current thread
Thread t = Thread.currentThread();
// The type of data structure actually stored
ThreadLocalMap map = getMap(t);
// Create a map and set it
if(map ! =null)
map.set(this, value);
else
createMap(t, value);
}
/ / getMap method
ThreadLocalMap getMap(Thread t) {
// Thred maintains a ThreadLocalMap
return t.threadLocals;
}
//createMap
void createMap(Thread t, T firstValue) {
// Instantiate a new ThreadLocalMap and assign it to the thread member variable threadLocals
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code
The set method can be used to see that the current thread saves a threadLocals of type ThreadLocalMap. The threadLocal is used as the key to store the value of the current data.
// Get method in ThreadLocal
public T get(a) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if(map ! =null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if(e ! =null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
returnresult; }}return setInitialValue();
}
// The getEntry method in ThreadLocalMap
private Entry getEntry(ThreadLocal
key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if(e ! =null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
Copy the code
ThreadLocalMap
Set method
//Entry is a static inner class of ThreadLocalMap that references ThreadLocal
// Let ThreadLocal and stored values form key-value relationships
static class Entry extends WeakReference<ThreadLocal<? >>{
/** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}//ThreadLocalMap constructorThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) {// An array of internal members, INITIAL_CAPACITY is a constant of 16
table = new Entry[INITIAL_CAPACITY];
// bit operation, the result is the same as taking the mold, calculate the need to store the location
//threadLocalHashCode is interesting
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
Copy the code
Each Thread holds an instance of ThreadLocalMap, threadLocals, which provides an Entry table for each Thread. All reads are performed by operating on this table.
Entry is a weak reference, and threadLocalMap uses a weak reference of ThreadLocal as the key. If a ThreadLocal has no strong external reference, the key (ThreadLocal) will be collected by GC. This will result in a null key in a ThreadLocalMap and a strong reference to the value, which will only be broken if the thead thread exits. For example, the presence of thread pools can lead to memory leaks.
If the key of a ThreadLocalMap is a weak reference to a ThreadLocal, the ThreadLocal will be reclaimed even if it is not manually deleted because ThreadLocalMap holds a weak reference to a ThreadLocal. When the key is null, the value is cleared the next time ThreadLocalMap calls the set(),get(), and remove() methods.
Remove () method
private void remove(ThreadLocal
key) {
// Use hash to calculate the table position of the current ThreadLocal variable
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
// Loop again to see if ThreadLocal is in the table array
for(Entry e = tab[i]; e ! =null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
// Call WeakReference's clear method to clear weak references to ThreadLocal
e.clear();
// Clear the element whose key is null
expungeStaleEntry(i);
return; }}Copy the code
Set method of ThreadLocalMap that clears value if key is null.
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
Copy the code
private void set(ThreadLocal
key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if(! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }Copy the code
One way to avoid memory leaks: Call ThreadLocal’s remove() method to clean up data after each use.
Usage scenarios of ThreadLocal
The most common scenario is when the interceptor parses user information and puts it into a ThreadLocal.
public class UserContext {
private static ThreadLocal<User> localUser = ThreadLocal.withInitial(() -> {
return new User();
});
public static void setUser(User user) {
localUser.set(user);
}
public static User getUser(a) {
return localUser.get();
}
Copy the code
The RequestContextHolder in Spring uses TreadLocal to store information.
Session-based Session management also uses TreadLoca, such as Treadlocal used internally by Shiro’s Subject.