1. Introduction of Threadlocal

A ThreadLocal provides a separate copy of a variable that is accessed by a thread. When a thread accesses this variable, it accesses its own copy of the data, thus making it thread-safe. ThreadLocal provides thread-isolation for variables.

2. How are threads isolated

Each Thread maintains a threadLocals variable, which is a ThreadLocalMap class.

public class Thread implements Runnable {... ./* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null; . . }Copy the code

ThreadLocalMap is a static inner class of the ThreadLocal class that sets and obtains key/value pairs. Each thread has a separate copy of ThreadLocalMap that stores values that can only be read and modified by the current thread. The ThreadLocal class implements thread isolation of variable access by operating on a per-thread unique copy of ThreadLocalMap.

public class ThreadLocal<T> {... .static class ThreadLocalMap {
		static class Entry extends WeakReference<ThreadLocal<? >>{
	            /** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}}}Copy the code

Set method

// The value is stored in a separate copy of the current Thread's own ThreadLocalMap
public void set(T value) {
        Thread t = Thread.currentThread();  // Get the current thread
        ThreadLocalMap map = getMap(t); // Get the ThreadLocalMap of the current thread (each thread has its own separate copy)
        if(map ! =null)
            map.set(this, value); // this, threadLocal as Key
        else
            createMap(t, value);  // If no, create one
    }
Copy the code

The Get method

public T get(a) {
        Thread t = Thread.currentThread();            // Get the current thread
        ThreadLocalMap map = getMap(t);              // Get the ThreadLocalMap of the current thread (each thread has its own separate copy)
        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

In summary, ThreadLocal stores the data that a thread needs to access in the thread object itself, thus avoiding multi-threaded contention and implementing thread isolation.

3. Memory leaks

3.1 a weak reference

Notice that ThreadLocal uses a weak reference as the key in the Entry of ThreadLocalMap. Why weak references? What’s the problem with using strong references? Let’s review Java references first

Strong references: An object that has a strong reference must not be collected by gc; Soft references are not reclaimed even when memory is low: they are not reclaimed when memory is low; When memory is low, weak references are reclaimed: they have a shorter lifetime than soft references. When the garbage collector scans this part of memory and finds a weak reference, it reclaims it regardless of whether there is enough memory. Virtual references: Do not determine the life cycle of an object. It can be recycled at any time.

3.1.1 Why Not Use Strong References? When we run out of ThreadLocal and set it to null, the ThreadLocalMap’s ThreadLocal cannot be reclaimed because it has strong references, which may cause memory leaks and not meet the user’s expectations. 3.1.2 Why Weak References are Used? To solve the problem of memory leaks using strong references. When we set it to null, the ThreadLocal in ThreadLocalMap can be easily collected by gc because it only has weak references.

3.2 Memory Leakage

Isn’t there a problem with weak references? There will still be memory leaks. When a ThreadLocal is reclaimed by gc due to weak references, the value in the key-value pair is not reclaimed because it is strongly referenced. If the current thread keeps working (for example, threads in the thread pool keep reusing), the value will always have a strong reference chain and cannot be reclaimed, resulting in a memory leak

CurrentThread—>ThreadLocalMap—>Entry—>value

3.3 How can I Resolve Memory Leaks

  1. After using ThreadLocal, call its remove() method to remove the corresponding Entry
try {
    threadLocal.set("Zhang"); ... }finally {
    localName.remove();
}
Copy the code
  1. When a threadLocal is reclaimed and the key is null, the ThreadLocalMap of the current Thread scans the entry with the key= NULL and sets the value to NULL each time the get, set, and remove methods are used to reclaim the threadLocal, avoiding memory leaks.
  2. At some level, we can declare ThreadLocal static to ensure that ThreadLocal is always strongly referenced and never recycled.

4. Application scenarios

  1. Imagine you have a scenario where the call link is very long. When you query for data in one of these sections, the last node needs to be used. What do you do at this time? Do you add this parameter to the input parameter of each interface, pass it in, and then use only the last node?

2. Imagine a scenario where you have a parameter that has nothing to do with the business, such as traceId, purely for log tracking. What are you doing adding a parameter that has nothing to do with business?

At this point, you can select ThreadLocal.

  1. Thread context is preserved in link tracing, which is very convenient to obtain some key information in a request flow
  2. In PageHelper, a small error occurs that adds a limit to SQL execution without adding startPage, possibly because the data in ThreadLocal has not been cleared
  3. The Spring framework uses ThreadLocal in transaction management to manage connections. Each thread is a separate connection, and the failure of a transaction cannot affect the process or results of other threads. Mybatis is also managed using ThreadLocal, as is SqlSession

5. Other issues

5.1 ThreadLocalMap Resolving Hash Conflicts

ThreadLocalMap does not use a linked list to resolve Hash collisions. Instead, ThreadLocalMap uses a linear detection method that determines the position of an element in the table array based on the hashcode value of the initial key. Then the fixed algorithm is used to find the next position of a certain step length, and judge in turn until the position can be found. ThreadLocalMap where the location is to find the next adjacent location.

/** * 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

5.2 extensions

InternalThreadLocal in Dubbo is a variant of ThreadLocal. When used with internalthreads, InternalThreadLocal provides higher access performance than common threads.

Internalthreads use arrays internally, and are located by subscript, which is very fast. If you have to expand, directly double the size of the array, done.

ThreadLocal, however, uses hashCode internally to fetch values, which is an extra step in the calculation process. In addition, hashCode is bound to encounter hash conflicts, and ThreadLocal has to resolve hash conflicts. After expansion, you have to rehash, which is slower

The data structure is different, and this is the essential difference between the two classes, which is why internalThreads perform better than ThreadLocal in the Dubbo scenario.

The idea of InternalThread is derived from Netty’s FastThreadLocal.