Understand the core principle of ThreadLocal

catalogue

1, ThreadLocal introduction

Core principles of InheritableThreadLocals

2, Analysis of ThreadLocal core method

1) initialValue()

2) setIntialValue()

3) set()

4) get()

How does ThreadLocal achieve replica isolation in a multithreaded environment?

3, Source code analysis of ThreadLocalMap

1, ThreadLocal introduction

ThreadLocal can ensure that in the case of multithreading, each thread has a unique copy of the object, and the copies between threads are independent and isolated from each other.

Each thread instance has two copies of ThreadLocal ThreadLocalMap: one is the threadLocals of the current thread, and the objects of each thread are stored in ThreadLocalMap. The other is the inheritableThreadLocals of the current thread. They are static internal classes, so there is only one of these two thredlocalmaps in the heap. inheritableThreadLocals can pass the variable value set by the current main thread to the inheritableThreadLocals of the child thread.

Core principles of InheritableThreadLocals

When the child thread initializes, it will check whether the inheritableThreadLocal of the main thread is empty. If not, it will copy a copy of the inheritablethreadlocales of the main thread to its own inheritablethreadlocales.

What exactly is inheritableThreadLocals? When you don't understand the principle, the best way is to debug the source code.

 

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        ...
         // Copy the inheritablethreadlocales of the main thread to the inheritablethreadlocales of the child thread
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

2, Analysis of ThreadLocal core method

1) initialValue()

Used to initialize the default value of threadLocal, which is null by default.

protected T initialValue() {
    return null;
}		

2) setIntialValue()

When the set() method is overridden, this method replaces the use of the set() method

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    // Get the threadLocalMap of the current thread. If it is empty, then createMap. If it is not empty, then update the value of the map.
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
} 

3) set()

Set a value in ThreadLocalMap for the current thread, and the type is custom T

public void set(T value) {
    // 1. Get the current thread
    Thread t = Thread.currentThread();
    // 2. Get the ThreadLocalMap of the current thread
    ThreadLocalMap map = getMap(t);
    // 3. Check whether the map is initialized. If it is initialized, overwrite the previous value, otherwise initialize ThreadLocalMap
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

4) get()

Gets the value of the current thread.

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

How does ThreadLocal achieve replica isolation in a multithreaded environment?

It is implemented through ThreadLocalMap.

3, Source code analysis of ThreadLocalMap

ThreadLocalMap is a static internal class in ThreadLocal class. Understanding ThreadLocalMap can get rid of the fog and see the light.

ThreadLocalMap Code:

  static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
}

When was ThreadLocalMap initialized?

When the set() method is called, then createMap will be executed.

When createMap is created, both the thread and value will be given to ThreadLocalMap.

How are Thread and value stored in ThreadLocalMap?

The value stored by each thread is placed in the value attribute of a static internal class called Entry, so value is the object class, and a thread corresponds to an Entry. In the threadLocalMap class, there is a table, which is used to store k and v of threadLocal of all threads (including the main thread and all sub threads), k is the thread object, and v is the value of object type.

If it is inheritableThreadLocal, the created map will also be added to the Entry[] table of the static internal class of ThreadLocalMap, so as to ensure that all threads (including the main thread and all child threads) can have a unique copy of variables.

Since all threads and copies will eventually be placed in the Entry [] of ThreadLocalMap, this ensures that the variables between each thread are independent.

 

Added by pasychosheep on Sat, 29 Jan 2022 14:49:51 +0200