InheritableThreadLocal源码解析

ThreadLocal在是多线程操作中一个常用的工具类,主要为每个线程提供一个本地副本,避免共享变量的产生。但是他有一个尴尬的情况,就是不支持父子线程之间的传递,但在实际工作中还是会有这样的需求,所以就出现了InheritableThreadLocal,因为之前了解过ThreadLocal的实现,所以对InheritableThreadLocal产生了好奇。

打开InheritableThreadLocal,继承了ThreadLocal,重写了几个方法,主要就是将Thread中的threadLocals替换成了inheritableThreadLocals,在贴张Thread源码的截图应该就清晰了

java 复制代码
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);
    }
}

其余的就跟ThreadLocal流程相同了,这样就很奇怪了,为什么重写了这几个方法就可以进行父子线程的传递,那答案就只能在Thread的实现上,不论是ThreadLocal或者InheritableThreadLocal在我的理解里,他们其实更像工具类,真正的实现线程上的交互还是在Thread进行的。

java 复制代码
public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
java 复制代码
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        // 获取当前线程,也就是当前Thread启动后的父线程
        Thread parent = currentThread();
        // java线程安全相关
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */
            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }
            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();
        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        g.addUnstarted();
        this.group = g;
        //是否是守护线程
        this.daemon = parent.isDaemon();
        //设置优先级
        this.priority = parent.getPriority();
        //线程名称
        this.name = name.toCharArray();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        // 赋值runnable
        this.target = target;
        setPriority(priority);
        //判断父线程是否构建了InheritableThreadLocals
        if (parent.inheritableThreadLocals != null)
            // 在当前ThreadL对象中构建InheritableThreadLocals对象,将父线程的InheritableThreadLocals对象赋值给当前ThreadL对象
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;
        /* Set thread ID */
        tid = nextThreadID();
    }

Thread在初始化的时候,会判断当前线程是否有inheritableThreadLocals,有的话就会在当前Thread类构建一个相同的inheritableThreadLocals对象,这里有点点绕的就是currentThread()却被称为父线程,因为构建Thread的时候,新的线程还没开启,所以currentThread()获取的线程还是原来的线程。点开createInheritedMap会发现他调用的是ThreadLocalMap方法,会发现有两个构造方法,但是目的是完全不同的。

java 复制代码
         /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;
        //构建新的ThreadLocalMap
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //初始16
            table = new Entry[INITIAL_CAPACITY];
            //hash
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
java 复制代码
      // 根据父线程的ThreadLocalMap构建新的ThreadLocalMap,新的ThreadLocalMap包含父类所有的ThreadLocalMap
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            //获取父类所有的map
            Entry[] parentTable = parentMap.table;
            // 获取父类map的长度
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                // 获取父类map的元素
                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 c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        // 防止hash冲突,找到空的位置插入
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

总的来说InheritableThreadLocal和ThreadLocal的增删改查的逻辑基本相同的,主要还是Thread中多了一个传递的逻辑。

相关推荐
2301_780789664 小时前
跨平台适配!SDK 游戏盾兼容全终端游戏场景
java·网络·ddos
学长毕设5 小时前
基于springboot的食品仓库管理系统
java·spring boot·后端
滴滴答答哒6 小时前
ASP.NET Core + FaceRecognitionDotNet 人脸识别实战:DI 单例、特征向量持久化与 1:N 比对
后端·asp.net
步行cgn6 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
龙亘川6 小时前
构建城市公共安全数字底座:亘川智城智慧安防系统全维度解读
java·大数据·开发语言·人工智能·信息可视化
金銀銅鐵6 小时前
[Java] 用 CLI 展示 EnumMap 增删键值对时的内部变化
java·源码·ai编程
秋天的怀念_David6 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
Raas1007 小时前
AI网关部署方案?从部署到上线全流程
java·开发语言·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品
niucloud-admin7 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
霸道流氓气质8 小时前
智能客服组件:分层意图识别、RAG Advisor 与人工转接状态机的 Vue3 + Spring AI 生产级实战
java·人工智能·spring