InheritableThreadLocal pass the parent thread to child thread thread local variables

1 concept

In Javamulti-threaded programming, through the ThreadLocalrealization of a thread-local variable mechanism through space for time to achieve the purpose thread isolation.

But now there is such a demand: a parent thread in thread local variables need to be passed to the child threads.

For this case, ThreadLocalit can not be completed because the ThreadLocalpreservation of thread-local variables, the parent thread of thread-local variables only he can get, can not get the child thread. The following code demonstrates:

package com.tao.springbootdemo.thread;

public class ParentAndChildThreadMain {

    public static void main(String[] args) {

        // 父线程
        Thread parentThread = new Thread(new Runnable() {

            // 父线程中的线程局部变量
            private ThreadLocal<String> threadLocal = new ThreadLocal<>();
            // private InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

            @Override
            public void run() {
                System.out.println("> 父线程中设置线程的本地变量");
                threadLocal.set("local variable");
                System.out.println("> 父线程中拿到的线程本地变量是:" + threadLocal.get());

                // 在父线程中再起一个子线程
                Thread childThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("> 子线程中获取父线程的本地变量");
                        System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
                    }
                });
                childThread.setName("子线程!");
                childThread.start();
            }
        });
        parentThread.setName("父线程!");
        parentThread.start();
    }
}

The output was:

> 父线程中设置线程的本地变量
> 父线程中拿到的线程本地变量是:local variable
> 子线程中获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:null

Process finished with exit code 0

So, how to solve this demand?

JavaProvides a InheritableThreadLocalsolved problem thread local variables passed in the parent-child thread! ! !

The following code demonstrates:

package com.tao.springbootdemo.thread;

public class ParentAndChildThreadMain {

    public static void main(String[] args) {

        // 父线程
        Thread parentThread = new Thread(new Runnable() {

            // 父线程中的线程局部变量
            // private ThreadLocal<String> threadLocal = new ThreadLocal<>();
            private InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

            @Override
            public void run() {
                System.out.println("> 父线程中设置线程的本地变量");
                threadLocal.set("local variable");
                System.out.println("> 父线程中拿到的线程本地变量是:" + threadLocal.get());

                // 在父线程中再起一个子线程
                Thread childThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("> 子线程中获取父线程的本地变量");
                        System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
                    }
                });
                childThread.setName("子线程!");
                childThread.start();
            }
        });
        parentThread.setName("父线程!");
        parentThread.start();

    }
}

The output obtained is:

> 父线程中设置线程的本地变量
> 父线程中拿到的线程本地变量是:local variable
> 子线程中获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:local variable

Process finished with exit code 0

We just ThreadLocalchanged InheritableThreadLocal, you will be able to take the child thread to thread-local variables of the parent thread.

2 Principle Analysis

2.1 class source code analysis

First look Threadlike:

public class Thread implements Runnable {
    ......

    /**
     * 与此线程相关的ThreadLocal值,保存线程本地变量。
     * 这个map由ThreadLocal类维护。
     */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * 与此线程相关的那些从父线程继承而来的ThreadLocal值。
     * 这个map由InheritableThreadLocal类维护。
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

As can be seen, threadLocalsand inheritableThreadLocalsmaintained by two instance fields, with each other independently of each other .

Then, look at InheritableThreadLocalthe source code:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * 该函数在父线程创建子线程,向子线程复制InheritableThreadLocal变量时使用
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * 由于重写了getMap,操作InheritableThreadLocal时,
     * 将只影响Thread类中的inheritableThreadLocals变量,
     * 与threadLocals变量不再有关系。
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * 类似于getMap,操作InheritableThreadLocal时,
     * 将只影响Thread类中的inheritableThreadLocals变量,
     * 与threadLocals变量不再有关系。
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}
  • InheritableThreadLocalInherited from ThreadLocal;
  • Rewriting the ThreadLocalthree functions;
  • Rewritten getMap(), createMap()function is very important, the operation is t.inheritableThreadLocals, these two functions in the call set(T value)and get()when the function has played a very important role;
  • And ThreadLocalaffect each other.

2.2 by value analysis

First, create a child thread starts from the parent thread.

Parent thread calls new Thread()to create a child thread:

Thread childThread = new Thread();

Call the Threadconstructor of the class:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        // 默认情况下,设置inheritThreadLocals为可传递
        init(g, target, name, stackSize, null, true);
    }

    /**
     * 初始化一个线程。
     * 此函数有两处调用:
     * 1、上面的 init(),不传AccessControlContext,inheritThreadLocals = true;
     * 2、传递AccessControlContext,inheritThreadLocals = false
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        
        ......(其他代码)
        
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        
        ......(其他代码)
    }
  • It can be seen when generating a sub-thread default mode inheritThreadLocals = true.

  • If at this time the parent thread inheritableThreadLocalsis not empty, then the parent thread inheritableThreadLocalsto child thread passes. If the parent thread is used InheritableThreadLocalto save the thread local variable, then call set(T value)time, the operation is the thread inheritableThreadLocals, so parent.inheritableThreadLocals != null.

  • Which, by calling ThreadLocal.createInheritedMap(parent.inheritableThreadLocals)to the parent thread inheritableThreadLocalspass and create a child thread inheritableThreadLocals.

View ThreadLocal.createInheritedMapSource function:

    // 创建线程的inheritableThreadLocals
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

    /**
     * 接收传进来的 parent.inheritableThreadLocals,
     * 构建一个包含parent.inheritableThreadLocals中所有键值对的ThreadLocalMap,
     * 该函数只被 createInheritedMap() 调用.
     */
    private ThreadLocalMap(ThreadLocalMap parentMap) {
        Entry[] parentTable = parentMap.table;
        int len = parentTable.length;
        setThreshold(len);
        table = new Entry[len];

        // 逐个复制parent.inheritableThreadLocals中的Entry
        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);
                    // 创建子线程中对应的Entry
                    Entry c = new Entry(key, value);
                    int h = key.threadLocalHashCode & (len - 1);
                    while (table[h] != null)
                        h = nextIndex(h, len);
                    // 保存
                    table[h] = c;
                    size++;
                }
            }
        }
    }

From the top of the source code analysis can be seen in the creation of the child thread, the parent thread in inheritableThreadLocalsthe corresponding ThreadLocalMapall in Entryall copied to a new ThreadLocalMap, and finally this will ThreadLocalMapbe assigned to the child threads inheritableThreadLocals.

When you call the parent thread in the child thread InheritableThreadLocal<String> threadLocalthe threadLocal.get()way to get the parent thread thread-local variable, view the source code:

    public T get() {
        Thread t = Thread.currentThread();
        // InheritableThreadLocal重写了getMap,返回的是线程的inheritableThreadLocals
        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 can be seen,

  • Because InheritableThreadLocalrewrite the class getMap(), it returns the thread inheritableThreadLocals.
  • So, in the sub-thread call get()to get the current thread is inheritableThreadLocalsthis ThreadLocalMap.
  • Finally, get()the return value is stored in a sub-thread of inheritableThreadLocalsvalues.

2.3 child thread will be updated passed from parent thread to thread local variables do?

Write code to test:

package com.tao.springbootdemo.thread;

public class ParentAndChildThreadMain {

    public static void main(String[] args) {

        // 父线程
        Thread parentThread = new Thread(new Runnable() {

            // 父线程中的线程局部变量
            // private ThreadLocal<String> threadLocal = new ThreadLocal<>();
            private InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

            @Override
            public void run() {
                System.out.println("> 父线程中设置线程的本地变量");
                threadLocal.set("local variable");
                System.out.println("> 父线程中拿到的线程本地变量是:" + threadLocal.get());

                // 在父线程中再起一个子线程
                Thread childThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("> 子线程中获取父线程的本地变量");
                        System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());
                        try {
                            System.out.println("> 子线程 sleep 5秒");
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("> 子线程中再次获取父线程的本地变量");
                        System.out.println("> 子线程中拿到的父线程本地变量是:" + threadLocal.get());

                    }
                });
                childThread.setName("子线程!");
                childThread.start();

                try {
                    System.out.println("> 父线程 sleep 2秒");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("> 父线程中再次设置线程的本地变量");
                threadLocal.set("AAA");
                System.out.println("> 父线程中拿到的改变后的线程本地变量是:" + threadLocal.get());
            }
        });
        parentThread.setName("父线程!");
        parentThread.start();
    }
}

Program output:

> 父线程中设置线程的本地变量
> 父线程中拿到的线程本地变量是:local variable
> 父线程 sleep 2秒
> 子线程中获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:local variable
> 子线程 sleep 5秒
> 父线程中再次设置线程的本地变量
> 父线程中拿到的改变后的线程本地变量是:AAA
> 子线程中再次获取父线程的本地变量
> 子线程中拿到的父线程本地变量是:local variable

Process finished with exit code 0

You can see, the child thread is not updated passed from parent thread over the thread-local variables! ! !

3 summary

InheritableThreadLocalMainly used in the creation of the child thread, the thread local variables need to automatically inherit parent thread to use.

发布了178 篇原创文章 · 获赞 152 · 访问量 61万+

Guess you like

Origin blog.csdn.net/hbtj_1216/article/details/100511851
Recommended