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.