【Java】子线程获取父线程ThreadLocal

前言:

        阅读这篇博客前,请确保你对ThreadLocal的使用场景和实现原理有一定了解。若对ThreadLocal的认知处于白纸阶段,可以先去看下这篇博客:【Java】ThreadLocal原理与使用场景-CSDN博客。

       本文主要聚焦于ThreadLocal在父子线程间传递的原理

原理:

Code:

package test;

public class InheritableThreadLocalTest {
    public static void main(String[] args) {

        User u1 = new User(1,"王祖贤");
        ThreadLocal threadLocal = new ThreadLocal<>();
        threadLocal.set(u1);


        User u2 = new User(2,"邱淑贞");
        InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal<>();
        inheritableThreadLocal.set(u2);


        Thread subThread = new Thread(() -> {
            User user1 = threadLocal.get();
            System.out.println("获取父线程的ThreadLocal:" + user1);


            User user2 = inheritableThreadLocal.get();
            System.out.println("获取父线程的InheritableThreadLocal:" + user2);
        }, "SubThread");

        subThread.start();
    }
}

代码解读:

        1、main线程(父线程)创建了一个User对象,将其保存到了ThreadLocal中。

        2、main线程(父线程)又创建了一个User对象,将其保存到了InheritableThreadLocal中。

        3、创建了一个名为SubThread的子线程,其任务逻辑就是去分别获取保存到ThreadLocal中的值,以及保存到InheritableThreadLocal中的值。

运行结果

【Java】子线程获取父线程ThreadLocal_第1张图片

        我们可以看到,子线程尝试获取ThreadLocal保存的值时,得到的是null;而尝试获取InheritableThreadLocal保存的值时,能正确的得到父线程保存的值。想要知道原因,必然需要清楚InheritableThreadLocal类的实现!

InheritableThreadLocal:

public class InheritableThreadLocal extends ThreadLocal {
    
    protected T childValue(T parentValue) {
        return parentValue;
    }

    
    ThreadLocalMap getMap(Thread t) {
       //返回的是Thread中的inheritableThreadLocals字段
       return t.inheritableThreadLocals;
    }

    
    void createMap(Thread t, T firstValue) {
        //初始化的是Thread中的inheritableThreadLocals字段
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

        通过源码我们可以看到,InheritableThreadLocal继承自ThreadLocal,只是重写了childValue、getMap、createMap三个方法。

        (1)getMap()

public void set(T value) {
        Thread t = Thread.currentThread();
        //getMap方法获取的是inheritableThreadLocals字段
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //createMap方法初始化的是inheritableThreadLocals字段
            createMap(t, value);
    }


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


public class Thread implements Runnable {

//成员变量,每个线程都有
ThreadLocal.ThreadLocalMap threadLocals = null;

//成员变量,每个线程都有
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

        重写了getMap方法后,使用set、get方法操作的不再是threadLocals字段,而是inheritableThreadLocals字段。

        (2)createMap()

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        //getMap方法获取的是inheritableThreadLocals字段
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //createMap方法初始化的是inheritableThreadLocals字段
            createMap(t, value);
        return value;
    }

        与上述一样,在线程第一次调用set、get方法时,初始化的是inheritableThreadLocals字段。

我们再来看看Thread是如何初始化的:

//默认构造
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }


private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }


private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals){
    
    //....    

    Thread parent = currentThread();

    //....

    //inheritThreadLocals:boolean类型(总是为true)
    //parent.inheritableThreadLocals:ThreadLocalMap
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            //如果父线程的inheritableThreadLocals不为null
            //则设置子线程的inheritableThreadLocals
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

}

        重点是inheritThreadLocals字段的初始化,所以init函数省去了很多无关代码。

        (3)createInheritedMap(ThreadLocalMap parentMap)

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal key = (ThreadLocal) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

        这段代码的主要作用是:将父线程ThreadLocalMap中的键值对复制到新的ThreadLocalMap 实例中,同时通过调用childValue方法(重写)更新值。

总结:

        1、子线程在初始化时,会查看父线程的inheritableThreadLocals字段是否为null,如果不为null,则会根据父线程的inheritableThreadLocals字段去初始化子线程的inheritableThreadLocals字段。

        2、InheritableThreadLocal类重写了getMap、createMap两个方法,在使用InheritableThreadLocal对象调用set、get等方法时,操作的是线程的inheritableThreadLocals字段。

你可能感兴趣的:(面试,java,jvm)