Source code interpretation of ThreadLocal

Posted by buckboru on Sun, 08 Dec 2019 16:34:14 +0100

I. Introduction

public class Thread implements Runnable {
    /* Slightly ahead  */

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    /* Slightly later  */
 }

First of all, we see that there is a property threadLocals in Thread. Its type is ThreadLocalMap, and its encapsulation type is default (indicating that it can only be seen in the package). jdk introduces it as follows: the ThreadLocal value related to this Thread, which is maintained by the ThreadLocal class. What do you mean? Let's see what ThreadLocalMap is!

public class ThreadLocal<T> {
   /* Slightly ahead  */

    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;
            }
        }
   /* Slightly later  */
   }
}

From the class definition, it can be seen that ThreadLocal supports generics, while ThreadLocalMap is an internal class of ThreadLocal, and the encapsulation type is also default (indicating that it can only be seen in the package). jdk introduces it as follows: ThreadLocalMap is a custom hash Map, which is only applicable to maintaining thread local values. In order to control the storage capacity and avoid memory leakage, hash table entries use weak references as keys (the life cycle of weak references is recycled until the next garbage collection). ThreadLocalMap uses the static internal class entry (which can be similar to the entry in Map) to store the actual key and value.

From the above descriptions, we can roughly think that ThreadLocal is a thread related class used to store and maintain thread local values.

II. Interpretation of set(T value) method

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

It can be seen that the set(T value) method does a very simple job, which is to maintain the threadlocations property of the Thread. If the property does not exist, create one with the current ThreadLocal instance as the key. If the property exists, assign a value directly.

III. interpretation of get() method

    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();
    }
    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;
    }
    protected T initialValue() {
        return null;
    }

The get() method is also very simple, that is, to get the value from the threadlocales property of Thread. If not, assign the value of initialvalue () to the threadLocals property of Thread and return it. The initialValue() method is a protected type method, which returns null by default. We can override it when creating ThreadLocal to represent the default value of all threads.

    // How java8 works
    ThreadLocal<Boolean> threadLocal1 = ThreadLocal.withInitial(() -> false);
    // 
    ThreadLocal<Boolean> threadLocal2 = new ThreadLocal<Boolean>() {
        @Override
        protected Boolean initialValue() {
            return false;
        }
    };

Four, summary

  • ThreadLocal is used to store the local value of the maintenance thread.
  • Similar to ThreadLocal, there is also an inheritable ThreadLocal. Inheritable ThreadLocal inherits from ThreadLocal. It is used to share common values between parent and child threads. The values set in the parent thread can be accessed in the child thread.
  • When we look at the ThreadLocalMap above, we know that the key is a weak reference, and the key will be recycled in gc, but the references of value and ThreadLocalMap will not be recycled. If there are many threads in this situation, and they have not been executed, memory leaks may occur. Therefore, when using ThreadLocal, try to call the remove() method of ThreadLocal.
  • When using the thread pool, after calling the set method of ThreadLocal, the remove method is not called. If the same thread calls the get method again, it may not get the value set at that time (because the threads of the thread pool are reused), which may cause program data exceptions and so on. Therefore, when using ThreadLocal, try to call ThreadLocal remove() method of.

Topics: Java JDK