Welcome to the public account: Wuxiaozhu

What is a ThreadLocal?

The Java API documentation defines ThreadLocal as follows:

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread accessing a variable (through its GET or set methods) has its own local variable, independent of the initialized copy of the variable. ThreadLocal instances are typically private static fields in a class that want to associate state with a thread (for example, a user ID or transaction ID).

So a ThreadLocal is a container class that limits the scope of an object to the current thread. In the case of multiple threads, when a local variable is accessed, it may be modified by another thread. ThreadLocal, on the other hand, provides a thread-closing technique that makes this variable exclusive to each thread. Like Mybatis level 1 cache and level 2 cache scope. Level 1 caching is scoped to a SqlSession and is used for queries within the same SqlSession. The scope of a ThreadLocal is the current Thread.

ThreadLocal used in those years

  1. A typical class for storing thread-unsafe utility classes is SimpleDateFormat. An example is a provision in the Ali Java development manual.

  1. Each thread needs to hold information similar to global variables (such as user information retrieved in the interceptor) that can be used directly by different methods, avoiding the hassle of passing parameters but not sharing them with multiple threads (because different threads get different user information).

  2. The most common use of ThreadLocal is for database connections, Session management, etc., such as Spring transactions. Transactions are tied to threads, and the Spring framework binds the current thread to a Jdbc when the transaction starts Connection, in the whole transaction process is the use of the thread binding Connection to perform database operations, to achieve the isolation of transactions. The Spring framework uses ThreadLocal to implement this isolation

public abstract class TransactionSynchronizationManager { / / thread binding resources, such as DataSourceTransactionManager binding: yes, it is a source of a Connection, in the process of the whole transaction execution / / all use the same Jdbc Connection private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources"); . }Copy the code
  1. Srping RequestContextHolder, DateTimeContextHolder, etc.

ThreadLocal source code parsing

Structure of ref;

The main properties

Public class ThreadLocal<T> {/** ThreadLocal hashCode, */ private final int threadLocalHashCode = nextHashCode(); private final int threadLocalHashCode = nextHashCode(); private static AtomicInteger nextHashCode = new AtomicInteger(); Private static final int HASH_INCREMENT = 0x61C88647; private static final int HASH_INCREMENT = 0x61C88647; */ private static int nextHashCode() {return nexthashcode.getAndAdd (HASH_INCREMENT); */ private static int NexthashCode.getAndAdd (HASH_INCREMENT); }Copy the code

ThreadLocalMap

ThreadLocalMap is a Map whose Key is ThreadLocal and whose Value is Value.

Static class Entry extends WeakReference<ThreadLocal<? 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; private int size = 0; private int threshold; Private void setThreshold(int len) {threshold = len * 2/3; } private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } ThreadLocalMap(ThreadLocal<? > firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }Copy the code

The get () method

How does ThreadLocal implement thread-isolation exclusivity for variables?

As you can see from the source code for the Thread class, each Thread has its own ThreadLocalMap, starting with NULL.

// There are two variables, threadLocals and inheritableThreadLocals, Variables are ThreadLocalMap ThreadLocal inner class types ThreadLocal. ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;Copy the code

The get() method retrieves the current Thread T and returns the threadLocals variable bound to Thread T via getMap(t). Is the above ThreadLocal. ThreadLocalMap threadLocals variables. If it is null, setInitalValue() is called. If it is not empty, threadLocalMap.entry is obtained through t. The threadLocals variable in the Thread class is assigned when the get()/set() methods of ThreadLocal are called.

    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) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if(e ! =null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                returnresult; }}return setInitialValue();
    } 
Copy the code

The map is saved to Thread T, not to ThreadLocal, which is the threadLocals variable of Thread T.

private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); return value; } /** * If the threadLocal of thread t returned by getMap(t) is null, ThreadLocals */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap getMap(Thread t) {// Returns the threadLocals of the current Thread. return t.threadLocals; }Copy the code

set()

The set method also gets the threadLocals variable of the current thread T. If not null, this takes the current thread T as the key and stores it in the current thread’s threadLocals pair with value. If it is empty, the createMap() method is called to create and bind.

public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); }Copy the code

Set () dissects in depth

private void set(ThreadLocal<? > key, Object value) { Entry[] tab = table; int len = tab.length; Int I = key.threadLocalHashCode & (len-1); For (Entry e = TAB [I]; 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);} // The current I position is null and can be used by the current thradLocal. int sz = ++size; // If array size >= expansion threshold (two thirds of array size) if (! CleanSomeSlots (I, sZ) &&sz >= threshold) // Rehash (); }Copy the code

The formula for calculating the index position of an array is hashCode & array size, and ThreadLocalMap resolves hash collisions using linear detection. Different from array + linked list + red-black tree implementation of HashMap. Linear detection method is based on the current position to calculate the next position, if there is a conflict, continue to find the next empty position.

InheritableThreadLocal

InheritableThreadLocal is introduced

Notice that the other variable in Thread is inheritableThreadLocals. What does this variable do? Take a look at the INTRODUCTION to the JDK documentation.

This class extends ThreadLocal to provide child threads with values that they inherit from their parent: When a child thread is created, the child thread receives the initial values of all inheritable thread-local variables to get the values that the parent thread has. In general, the value of the child thread is the same as the value of the parent thread; However, by overriding the childValue method in this class, the child thread’s value can be an arbitrary function of the parent thread’s value.

Thread-private variables declared by ThreadLocal are not available to children of that thread, and thread-private variables declared by InheritableThreadLocal are available to children. In short, InheritableThreadLocal allows child threads to inherit values from parent threads, and ThreadLocal doesn’t. But changes made by child threads to variables are also invisible to the parent thread.

public class Demo { public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); public static InheritableThreadLocal<Integer> inheritableThreadLocal = new InheritableThreadLocal<Integer>(); public static void main(String[] args) throws InterruptedException { threadLocal.set(1); inheritableThreadLocal.set(2); New Thread(()-> {system.out.println (" threadLocal:"+ threadlocal.get ())); / / modify values in the child thread inheritableThreadLocal. Set (3); System. The out. Println (" a thread inheritableThreadLocal: "+ inheritableThreadLocal. The get ()); }).start(); Thread.sleep(1000); System.out.println(" threadLocal:"+ threadlocal.get ()); System. The out. Println (" in the main thread inheritableThreadLocal: "+ inheritableThreadLocal. The get ()); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the console output -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the child thread threadLocal: null child thread inheritableThreadLocal: 3 the main thread of threadLocal: 1 main Thread inheritableThreadLocal: 2Copy the code

[InheritableThreadLocal

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
Copy the code

The new Thread() release will eventually call the init method below. This method takes the inheritableThreadLocals variable of the parent thread and assigns it to the current thread.

Private void init(ThreadGroup G, Runnable Target, String Name, Long stackSize, AccessControlContext ACC) { Because mainly want to see the following lines...... if (parent.inheritableThreadLocals ! = null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); . }Copy the code

Considerations when using ThreadLocal

A memory leak

Each Thread has its own variable of type ThreadLocalMap, threadLocals, whose key is a weakly-referenced instance of ThreadLocal, and whose value is a copy of the Thread variable. The reference relationship between these objects is shown below, where solid lines are strong references and dotted lines are weak references.

Once GC occurs, weakly referenced objects are reclaimed and there is an Entry with a null key in ThreadLocalMap. If the Thread does not complete the execution, there will always be a strong reference chain for the values of these entries with null keys: Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value cannot be reclaimed, resulting in memory leakage.

  • How do you avoid memory leaks? When ThreadLocal is exhausted, the remove() method is called. Clear key and value.
private void remove(ThreadLocal<? > key) { 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)]) { if (e.get() == key) { e.clear(); // referent = null; Remove the key expungeStaleEntry (I); // Clear value return; } } } private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Omit line N here, rehash...... }Copy the code

Thread reuse issues in thread pools

When using thread pools to perform multithreading operations, we need to consider thread reuse, because instead of creating new threads for each execution, we often reuse existing threads, causing different execution tasks to get variables from the previous thread from the ThreadLocal. The solution is again to call the remove() method after the thread task has finished executing.

conclusion

ThreadLocal provides thread-local variables that have separate copies in multiple threads. It is he who provides this isolation technique that allows us to fetch the variables we need through the current thread. It prevents us from passing parameters through layers during development. Low coupling, simplifying our development. ThreadLocal is ubiquitous in the framework. So it’s time to conquer him!