Brief introduction:

ThreadLocal is used to provide thread-level variables that are visible only to the current thread. Compare that with the “use locks to control the order of access to shared variables” solution. ThreadLocal avoids the race problem by using a space-for-time scheme, where each thread has its own variable.

This leads to the first question: how do threads maintain their own copies of variables

How do threads maintain their own copies of variables?

1. Take a look at the Thread class first
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

Thread has a threadLocals property that represents the Thread’s local variable. This property is of type ThreadLocal. ThreadLocalMap

2. What’s ThreadLocalMap:

ThreadLocalMap is an internal class of ThreadLocal. It is a map-like structure that stores k-V structure data and encapsulates K-V with Entry. The difference is that the Entry K of a ThreadLocalMap can only be an object of type ThreadLocal and is a weak reference.

 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

Thread that is by a kind of Map data structure ThreadLocal. ThreadLocalMap variables to store your own thread.

ThreadLocal. ThreadLocalMap when initialization? ThreadLocal. ThreadLocalMap how to save the values?Copy the code
3. The ThreadLocal in this respect

ThreadLocalMap assigns values, and the entry to the value operation is in its external class ThreadLocal.

Sett (v) calls ThreadLocalMap’s set(this,v) method to store values. (Similar to Map’s put(k,v) method)

public void set(T value) {
    //1. Get the current thread instance
    Thread t = Thread.currentThread();
    //2. Obtain the ThreadLocalMap object from the current thread instance
    ThreadLocalMap map = getMap(t);
    if(map ! =null)
        //3. If Map is not null, the current threadLocl instance is stored as key and value is value
        map.set(this, value);
    else
        //4. If map is null, create a new ThreadLocalMap and store value
        createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Copy the code

ThreadLocalMap getEntry(this) = getEntry(this) = getEntry(this)

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();
    }

private T setInitialValue(a) {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
}
protected T initialValue(a) {
        return null;
}
Copy the code

As can be seen from the code:

  • Operation thread ThreadLocalMap attribute for the first time, will initialize a ThreadLocal. ThreadLocalMap,set(v)The KV data with parameter Value will be stored,get()Will be deposited tonullIs value’s KV data.
  • ThreadLocal. ThreadLocalMap deposit value operator is ThreadLocal set (v) method, and is the key with the ThreadLocal variable, parameter value.
  • ThreadLocal. ThreadLocalMap operator is ThreadLocal values. The get method (v), the key to the current ThreadLocal variable

We can see this visually at the code level:

ThreadLocal threadLocal1 = new ThreadLocal();

// If the thread is assigned a value for the first time, this is similar
// Map map = new HashMap(); Map. put(threadLocal1," first assignment ")
threadLocal1.set("First assignment of a variable");// similar to map.put(threadLocal1," first assignment ")

threadLocal1.set("Variable second assignment");// similar to map.put(threadLocal1," first assignment ")

System.out.println(threadLocal1.get());/ / similar to the map. The get (threadLocal1)Output: Variable second assignmentCopy the code

The nature of the thread’s local variables is now clear. Is the Thread with a similar Map of ThreadLocal. ThreadLocalMap data structure to store the ThreadLocal variable types as Key value, using ThreadLocal to access to delete, ThreadLocalMap operation.

  • When we define a ThreadLocal variable, we are defining a Key.
  • When we call set (v) method, is to the current ThreadLocal variables as the key, incoming parameters for the value, to the ThreadLocal. ThreadLocalMap save data.
  • When we call the get () method, which is at the current ThreadLocal variables as the key, from ThreadLocal. ThreadLocalMap take corresponding data

conclusion

Thread with a similar Map of ThreadLocal. ThreadLocalMap data structure to store the ThreadLocal variables of type as the Key values, using ThreadLocal to access delete, ThreadLocalMap operation

TreadLocal is a thread-local variable. It is helpful to understand TreadLocal from a thread’s perspective

extension

1. Hash conflict resolution of ThreadLocalMap

Linear detection is used to calculate the hash value according to the key. If there is a conflict, the detection is backward, starting from 0 at the end of the hash table until a suitable position is found.

This algorithm also determines that ThreadLocalMap is not suitable for storing large amounts of data.

2. Capacity expansion of ThreadLocalMap is abnormal

The initial size of threadLocalMap is 16 and the loading factor is 2/3. When the size is greater than threshold, the threadLocalMap will be expanded.

During capacity expansion, create an array twice the length of the original array, iterate over the entries in the old array and insert them into the new hash array. During capacity expansion, the value of the entry with the null key is set to NULL. In order to recycle memory, reduce the memory leak problem.