ThreadLocal的plus版--InheritableThreadLocal

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

一:简述

我们知道ThreadLocal可以实现线程级别下的数据隔离,但是如果需要将当前线程的值传递给子线程,是需要自己去写逻辑实现的,会比较复杂。而InheritableThreadLocal帮助我们解决了这个问题。本篇文章就和大家一起聊聊InheritableThreadLocal。

二:InheritableThreadLocal的原理

1.数据怎么存储

我们可以看到InheritableThreadLocal继承了ThreadLocal,并且重写了ThreadLocal的getMap()以及createMap()方法。getMap()返回Thread类的inheritableThreadLocals成员变量,createMap()方法的作用是创建一个ThreadLocalMap并且赋值给当前线程的inheritableThreadLocals,所以InheritableThreadLocal的get(),set(),remove()方法是继承自ThreadLocal,实现原理和ThreadLocal一样,只不过ThreadLocal数据是存储在Thread的threadLocals成员变量中,而InheritableThreadLocal是存储在Thread的inheritableThreadLocals成员变量中。

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

    // 返回当前线程的inheritableThreadLocals成员变量
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    //创建一个ThreadLocalMap 并且赋值给inheritableThreadLocals
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}
复制代码

1643522509(1).png

注:ThreadLocal原理在我的另外一篇文章讲的很彻底,这里就不在阐述了,原文地址:TheadLocal的实现原理

2.数据是怎么传递给子线程的?

在线程的构造函数中,调动了init()方法,在init()方法中调用createInheritedMap()方法初始化子线程的inheritableThreadLocals,并且将父线程中的inheritableThreadLocals的值取出来赋值给子线程的inheritableThreadLocals。

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) {
        ...其他无关源码 一些赋值操作 为了篇幅这里省略了
        
        //inheritThreadLocals默认是true 
        //如果需要传递并且父线程的inheritableThreadLocals不为空 
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        // 那么就初始化inheritableThreadLocals并且把父线程的inheritableThreadLocals中的值传递给子线程
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }
复制代码

接下来我们分析createInheritedMap()方法

createInheritedMap()

createInheritedMap()方法调用了ThreadLocalMap的有参构造函数。

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
复制代码

在构造函数中,遍历了父线程的inheritableThreadLocals,然后遍历父线程inheritableThreadLocals的Entry数组,重新封装成Entry,并且计算数组下标放入到子线程的inheritableThreadLocals中。这也就把数据从父线程传递给了子线程。

private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];
            //遍历父线程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();
                    //如果key为null 代表是无效数据 不需要传递给子线程
                    if (key != null) {
                        //取出父线程的value
                        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++;
                    }
                }
            }
        }
复制代码

总结:

InheritableThreadLocal的数据是存储在inheritableThreadLocals中的,而Thread在利用构造函数实例化的时候会将父线程的inheritableThreadLocals中的值复制一份给自己,保存在自己的inheritableThreadLocals中,这样就完成了父子线程之间的数据传递。

如果文章对你有帮助,那么点个赞再走吧.

Guess you like

Origin juejin.im/post/7061779385627443208