46 How Are Multiple Thread Locals Stored Within Thread's Threadlocals

The ThreadLocalMap is a member variable of a Thread object. It functions as a map to store key-value pairs. The keys in the ThreadLocalMap are references to ThreadLocal objects, and the values are objects that the ThreadLocal is storing, such as user objects.

Each Thread object contains a single ThreadLocalMap. The ThreadLocal objects are associated with their corresponding values in the ThreadLocalMap. However, it’s important to note that the ThreadLocal objects themselves are not stored in the ThreadLocalMap. Instead, they serve as keys to access the corresponding values in the map.

Let’s analyze the source code to further understand the implementation.

The get method #

The get method of ThreadLocal retrieves the value associated with the ThreadLocal object. Here is the source code:

public T get() {
    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;
            return result;
        }
    }
    return setInitialValue();
}

As you can see, the method first obtains a reference to the current Thread using Thread.currentThread(). It then calls the getMap method with this Thread object to retrieve the corresponding ThreadLocalMap.

Next, there is an if (map != null) statement. If the map is not null, it means that ThreadLocalMap has been previously created for this thread. In this case, the method retrieves the Entry from the ThreadLocalMap using the this reference (i.e., the reference to the current ThreadLocal object). It then obtains the value from this Entry and returns it as the result.

If the map is null, it means that the ThreadLocalMap has not been created for this thread. In this case, the setInitialValue method is called to create and initialize the ThreadLocalMap.

It’s important to note that the ThreadLocalMap is stored within the Thread object, not within the ThreadLocal object.

The getMap method #

Let’s take a look at the getMap method, which is used by the get method to retrieve the ThreadLocalMap from the Thread object. Here is the source code:

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

The getMap method simply returns the threadLocals member variable of the Thread object. This member variable is where the ThreadLocalMap is stored. We can see that this method clearly indicates the relationship between Thread and ThreadLocalMap, showing that ThreadLocalMap is a member variable of the thread. The purpose of this method is to obtain the ThreadLocalMap object within the current thread. Each thread has a ThreadLocalMap object, and the name of this object is threadLocals, with an initial value of null. The code is as follows:

ThreadLocal.ThreadLocalMap threadLocals = null;

The set method #

Now let’s take a look at the set method, its source code is as follows:

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

The purpose of the set method is to save the value we want to store. As can be seen, first, it still needs to obtain the reference of the current thread and use this reference to obtain the ThreadLocalMap. Then, if map == null, it creates this map, and when map != null, it uses the map.set method to set the value.

It can be seen that the two parameters passed in map.set(this, value) , the first parameter is this, which refers to the current ThreadLocal, which once again reflects that the key type in ThreadLocalMap is ThreadLocal. The second parameter is the value we passed in, so this key-value pair can be saved in ThreadLocalMap.

ThreadLocalMap class, which is Thread.threadLocals #

Now let’s take a look at the ThreadLocalMap class. The following code excerpt is taken from the ThreadLocalMap class defined in the ThreadLocal class:

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;
        }
    }
    private Entry[] table;
    //...
}

The ThreadLocalMap class is a member variable in the Thread class of each thread. The most important part is the Entry inner class extracted from the code. In ThreadLocalMap, there will be an Entry array named table. We can understand Entry as a map, with the key-value pair as follows:

  • Key: the current ThreadLocal
  • Value: the actual variable that needs to be stored, such as the user object or simpleDateFormat object.

Since ThreadLocalMap is similar to Map, it has a series of standard operations including set, get, rehash, and resize, just like HashMap. However, although the ideas are similar to HashMap, the specific implementation may be slightly different.

For example, one difference is that we know when HashMap encounters hash collisions, it uses chaining (linked list). It first hashes the object into a corresponding cell, and if there is a collision, it chains down in the form of a linked list, as shown in the following image:

img

But the way ThreadLocalMap handles hash collisions is different. It uses linear probing. If a collision occurs, it does not chain down in the form of a linked list but continues to look for the next empty cell. This is the difference between ThreadLocalMap and HashMap in handling collisions.

The above is the content of this lesson.

In this lesson, we mainly analyzed the relationship between the three very important classes: Thread, ThreadLocal, and ThreadLocalMap. Using diagrams to represent the relationship: each Thread has a ThreadLocalMap, and the key of ThreadLocalMap is ThreadLocal, which is used to store and maintain the content in this way. Afterwards, we analyzed the source code of some important methods for ThreadLocal.