Detailed explanation of InheritableThreadLocal

Table of Contents

Preface

text

1. Class ThreadLocal cannot implement value inheritance

2. Use InheritableThreadLocal to reflect the value inheritance feature

3. Execution process of value inheritance feature in source code

4. The parent thread has a new value, but the child thread still has the old value: immutable type

5. The child thread has the latest value, and the parent thread still has the old value.

6. Sub-threads can sense changes in object attribute values: variable type

7. Rewrite the childValue method to process inherited values


Foreword

Using the class InheritableThreadLocal, you can obtain the value inherited from the parent thread in the child thread, but ThreadLocal does not have it, so let us continue to understand.

Text

1. Class ThreadLocal cannot implement value inheritance

Create a new test case:

public class ThreadLocalNoExtends {
    static class Tools{
        public static ThreadLocal t1 = new ThreadLocal();
    }
    static class ThreadA extends Thread{
        @Override
        public void run() {
            try{
                for (int i = 0; i < 10; i + + ) {
                    System.out.println("Value in ThreadA thread=" + Tools.t1.get());
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            for (int i = 0; i < 10; i + + ) {
                if (Tools.t1.get() == null){
                    Tools.t1.set("This value is put by the main thread!");
                }
                System.out.println("Value in Main thread=" + Tools.t1.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA a = new ThreadA();
            a.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The running result is as shown in the figure:

Because the man thread created the ThreadA thread, the main thread is the parent thread of the ThreadA thread. It can be found from the running results that since the ThreadA thread does not inherit the main thread, ThreadLocacl does not have the value inheritance feature. At this time, the InheritableThreadLocal class must be used to replace it.

2. Use InheritableThreadLocal to reflect the value inheritance feature

Use the InheritableThreadLocal class to allow child threads to inherit values from the parent thread.

Code:

public class InheritableThreadLocal {
    static class Tools{
        public static java.lang.InheritableThreadLocal t1 = new java.lang.InheritableThreadLocal();
    }
    static class ThreadA extends Thread{
        @Override
        public void run() {
            try{
                for (int i = 0; i < 10; i + + ) {
                    System.out.println("Value in ThreadA thread=" + Tools.t1.get());
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try{
            for (int i = 0; i < 10; i + + ) {
                if (Tools.t1.get() == null){
                    Tools.t1.set("This value is put by the main thread!");
                }
                System.out.println("Value in Main thread=" + Tools.t1.get());
                Thread.sleep(100);
            }
            ThreadA a = new ThreadA();
            a.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

operation result:

The value obtained by the ThreadA child thread is inherited from the parent thread main.

3. Execution process of value inheritance feature in source code

Using InheritableThreadLocal can indeed achieve the feature of value inheritance, so how to implement this feature in the JDK source code? Perform analysis.

1) First take a look at the source code of the InheritableThreadLocal class, as follows:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created. This method is called from within the parent
     * thread before the child is started.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

There are three methods in the source code of the InheritableThreadLocal class. These three methods are all obtained by rewriting the method of the same name in the parent class ThreadLocal. Because @Override is not used for identification in the source code, so in the initial analysis If you don’t pay attention, the process will be quite convoluted.

These three core methods in the InheritableThreadLocal class are all obtained by rewriting the methods in the ThreadLocal class. The source codes of these three methods in the ThreadLocal class are as follows:

 /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    /**
     * Method childValue is visibly defined in subclass
     * InheritableThreadLocal, but is internally defined here for the
     * sake of providing createInheritedMap factory method without
     * needing to subclass the map class in InheritableThreadLocal.
     * This technique is preferable to the alternative of embedding
     * instanceof tests in methods.
     */
    T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

As can be seen from the source code, the ThreadLocal class operates threadLocals instance variables, and the InheritableThreadLocal class operates inheritableThreadLocals instance variables. These are two variables.

2) Go back and continue to look at the main thread in the main() method to execute the InheritableThreadLocal.set() method. The source code is as follows:

Calling the set() method in the InheritableThreadLocal object actually Just call the set() method in the ThreadLocal class, because InheritableThreadLocal does not override the set() method.

3) Let’s analyze the set() method in the ThreadLocal class. The source code is as follows:

 /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value. Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     * this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    }

When executing the set() method in the ThreadLocal class, two methods have been overridden by the InheritableThreadLocal class, namely getMap(t) and createMap(t,value). Be sure to note that when executing these two methods, call The getMap(t) method and createMap(t,value) method are overridden in the InheritableThreadLocal class. Take a look again at the source code for overriding these two methods in the InheritableThreadLocal class.

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
  
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

  
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

4) By looking at the source code of the getMap(Thread t) method and createMap(Thread t,T firstValue) method in the InheritableThreadLocal class, an important knowledge point can be clarified, that is, data is no longer stored in the ThreadLocalMap threadLocals in the Thread class. Instead, data is stored in ThreadLocal.ThreadLocalMap inheritableThread. These two objects are declared as follows in the Thread class.

The above analysis clarifies a knowledge point, that is, the main thread stores data into the inheritableThreadLocal object. The object inheritableThreadLocals is the container for storing data. So how does the child thread inherit the value of the inheritableThreadLocals object in the parent thread?

5) The idea of this implementation is that when creating the child thread ThreadA, the child thread actively refers to the inheritableThreadLocals object value in the parent thread main. The source code is as follows:

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
      ``````
        if (inheritThreadLocals & amp; & amp; parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
      ````
    }

Because the init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) method is called by the constructor of Thread, in new ThreadA(), init will be automatically called inside the Thread.java source code (ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) method.

The last parameter in the init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) method represents whether the current value will be inherited from the parent thread. Because this value is always passed true , the value is inherited every time. The source code for passing true is in the private void init(ThreadGroup g, Runnable target, String name, long stackSize) method. The source code is as follows:

 /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

This process is the method private void init(ThreadGroup g, Runnable target, String name, long stackSize).

Call the method init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) and always pass true to the last parameter, that is, the last parameter inheritThreadLocals is always true.

6) Execute the if statement in the init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) method as shown below.

 if (inheritThreadLocals & amp; & amp; parent.inheritableThreadLocals != null)

If the value of inheritThreadLocal on the left side of the operator & amp; & amp; is true, the expression on the right side of & amp; & amp; parent.inheritableThreadLocals != null will be evaluated.

When storing data to inheritableThreadLocals in the main thread, because the object inheritableThreadLocals is not empty, both sides of the & amp; & amp; operator are true. Then the program continues to run and assigns the inheritableThreadLocals object variable of the current thread. The code is as follows.

this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

7) This in the code this.inheritableThreadLocals is the object of the current Thread.java class. The purpose of executing the create-InheritedMap() method is to first create a new ThreadLocalMap object, and then assign the ThreadLocalMap object to the inheritableThreadLocals variable in the ThreadA object. The source code of the createInheritedMap() method is as follows.

 /**
     * Factory method to create map of inherited thread locals.
     * Designed to be called only from Thread constructor.
     *
     * @param parentMap the map associated with parent thread
     * @return a map containing the parent's inheritable bindings
     */
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

8) Let’s continue to analyze the core source code in the new ThreadLocalMap(parentMap) construction method.

 /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len]; //New Entry[] array

            for (int j = 0; j < len; j + + ) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value); //In order to allow programmers to overwrite it themselves, Object value = e.value is not written.
                        Entry c = new Entry(key, value); //Instantiate a new Entry object
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c; //Copy the data in the parent thread to the new array
                        size + + ;
                    }
                }
            }
        }

In the source code of the ThreadMap class constructor, the child thread creates a new table = new Entry[len]; object to store data, which originates from the parent thread.

The most critical code is as follows (calling this function here to give e.value will return e.value. Instead of directly writing Object value = e.value, a function is used, which gives the programmer the opportunity to rewrite. Continue below If you understand it, you will know why it is done):

 Object value = key.childValue(e.value);

Since the value data type can be immutable or mutable, two distinct results can occur.

Let’s start with the test of immutable data types:

public class Test {
    public static void main(String[] args) {
        String a = "abc";
        String b = a ;
        System.out.println(a + " " + b);
        a = "xyz";
        System.out.println(a + " " + b);
    }
}

operation result:

The String data type is immutable, and assigning new constant values to String will open up new memory space.

Let’s take a look at the testing of mutable data types.

public class Test {
    static class Userinfo{
        public String username;

        public Userinfo(String username) {
            super();
            this.username = username;
        }
    }
    public static void main(String[] args) {
        Userinfo userinfo1 = new Userinfo("I am the old value");
        Userinfo userinfo2 = userinfo1;
        System.out.println(userinfo1.username + " " + userinfo2.username);
        userinfo1.username = "I am the new value";
        System.out.println(userinfo1.username + " " + userinfo2.username);
    }
}

operation result:

The content of the custom Userinfo data type is variable. The objects userinfo1 and userinfo2 refer to the Userinfo class object at the same address. If the properties of one object are changed, the other one can also sense it.

If value in e.value is an immutable data type, the main thread uses the InheritableThreadLocal class to perform the set(String) operation. When the sub-thread object is created and started, the data in the sub-thread is the old data of the main thread. Since the String data type is immutable, the main thread and the child thread have their own String storage spaces, but the values in the spaces are the same. When the main thread uses new String data, it only changes the value in the String space of the main thread, and the child thread still uses the old String data type, which is immutable.

If e.value is a mutable data type, the main thread uses the InheritableThreadLocal class to perform the set(Userinfo) operation. When the sub-thread object is created and started, the Userinfo object owned by the main thread and the sub-thread is the same. When the main thread changes the attribute value in Userinfo, the child thread can immediately obtain the latest attribute value. As long as the main main thread changes the attribute value in Userinfo, the child thread can sense it.

4. The parent thread has a new value, but the child thread still has the old value: immutable type

new test case

public class InheritableThreadLocal {
    static class Tools {
        public static java.lang.InheritableThreadLocal t1 = new java.lang.InheritableThreadLocal();
    }

    static class ThreadA extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i + + ) {
                    System.out.println("Value in ThreadA thread=" + Tools.t1.get());
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        if (Tools.t1.get() == null) {
            Tools.t1.set("This value is put by the main thread!"); //Executed here.
        }
        System.out.println("Value in Main thread=" + Tools.t1.get());
        Thread.sleep(100);
        ThreadA a = new ThreadA();
        a.start();
        Thread.sleep(5000);
        Tools.t1.set("This value is put by the main thread newnewnewnew");
    }
}

After the program runs, the sub-thread still holds the old data, and the printed results are as follows:

5. The child thread has the latest value, but the parent thread still has the old value

Test code:

import java.lang.InheritableThreadLocal;

public class InheritableThreadLocal102 {
    static class Tools {
        static InheritableThreadLocal t1 = new InheritableThreadLocal();
    }

    static class ThreadA extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i + + ) {
                    System.out.println("Value in ThreadA thread=" + Tools.t1.get());
                    Thread.sleep(1000);
                    if (i == 5) {
                        Tools.t1.set("I am the latest value of ThreadA's newnewnew");
                        System.out.println("ThreadA already has the latest value--------------------");
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        if (Tools.t1.get() == null) {
            Tools.t1.set("This value is put by the main thread");
        }
        System.out.println("Value in Main thread=" + Tools.t1.get());
        Thread.sleep(100);
        ThreadA a = new ThreadA();
        a.start();
        Thread.sleep(3000);
        for (int i = 0; i < 10; i + + ) {
            System.out.println("main end get value=" + Tools.t1.get());
            Thread.sleep(1000);
        }
        ;
    }
}

The running results are as follows:

There is always old data in the main thread.

6. Sub-threads can sense changes in object attribute values: variable type

The previous ones were all experiments on using the String data type for inheritance features in the main thread. If the child thread inherits a variable object from the parent thread,

data type, then the child thread can get the attribute value in the latest object.

public class InheritableThreadLocal103 {
    static class Userinfo{
        private String username;

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }
    }
    static class Tools{
        public static java.lang.InheritableThreadLocal<Userinfo> t1 = new java.lang.InheritableThreadLocal<>();
    }
    static class ThreadA extends Thread{
        @Override
        public void run() {
            try{
                for (int i = 0; i < 10; i + + ) {
                    Userinfo userinfo = Tools.t1.get();
                    System.out.println("Value in ThreadA thread=" + userinfo.getUsername() + " " + userinfo.hashCode());
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Userinfo userinfo = new Userinfo();
        System.out.println("A userinfo " + userinfo.hashCode());
        userinfo.setUsername("China");
        if (Tools.t1.get() == null){
            Tools.t1.set(userinfo);
        }
        System.out.println(" Value in Main thread=" + Tools.t1.get().getUsername() + " " + Tools.t1.get().hashCode());
        Thread.sleep(100);
        ThreadA a = new ThreadA();
        a.start();
        Thread.sleep(5000);
        Tools.t1.get().setUsername("United States");
    }
}

The running result of the program is that ThreadA has obtained the latest attribute value of the userinfo object. The running result is as follows:

If a new Userinfo object is put in at the end of the main() method, the result printed by the ThreadA thread will always be China. This is because ThreadA always refers to the Userinfo object corresponding to China, not the Userinfo object corresponding to the new version of the United States, so it is still consistent with “the parent thread has the latest value, and the child thread still has the old value.” The code is as follows:

public static void main(String[] args) throws InterruptedException {
        Userinfo userinfo = new Userinfo();
        System.out.println("A userinfo " + userinfo.hashCode());
        userinfo.setUsername("China");
        if (Tools.t1.get() == null){
            Tools.t1.set(userinfo);
        }
        System.out.println(" Value in Main thread=" + Tools.t1.get().getUsername() + " " + Tools.t1.get().hashCode());
        Thread.sleep(100);
        ThreadA a = new ThreadA();
        a.start();
        Thread.sleep(5000);
        Userinfo userinfo2 = new Userinfo();
        userinfo2.setUsername("United States");
        System.out.println("B userinfo " + userinfo2.hashCode());
        Tools.t1.set(userinfo2);
    }

operation result:

7. Rewrite the childValue method to process inherited values

Overriding childValue can achieve inheritance while further processing the value.

Create new test case:

public class InheritableThreadLocal {
    static class InheritableThreadLocalExt extends java.lang.InheritableThreadLocal{
        @Override
        protected Object childValue(Object parentValue) {
            return parentValue + "I added it in the child thread~!";
        }

        @Override
        protected Object initialValue() {
            return new Date().getTime();
        }
    }
    static class Tools {
        public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
    }

    static class ThreadA extends Thread {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i + + ) {
                    System.out.println("Value in ThreadA thread=" + Tools.t1.get());
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        try{
            for (int i = 0; i < 10; i + + ) {
                if (Tools.t1.get() == null) {
                    Tools.t1.set("This value is put by the main thread!"); //Executed here.
                }
                System.out.println("Value in Main thread=" + Tools.t1.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA a = new ThreadA();
            a.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

The running result is as shown in the figure:

By overriding the childValue() method, the child thread can process and modify the value inherited by the parent thread.

Execute the InheritableThreadLocalExt.set() method at any time in the child thread so that the child thread has the latest value. In addition, by overriding the childValue() method, the child thread will also get the latest value. The difference between these two points is that the child thread can execute the InheritableThreadLocalExt.set() method any number of times at any time, so that it always holds a new value. However, when overriding the childValue() method, it can only be used when creating a child thread. Valid for one time only.