首页 > 其他分享 >【InheritableThreadLocal】InheritableThreadLocal的实现机制和原理

【InheritableThreadLocal】InheritableThreadLocal的实现机制和原理

时间:2023-03-18 15:35:31浏览次数:40  
标签:InheritableThreadLocal ThreadLocalMap 创建 inheritableThreadLocals 线程 原理 机制 null

1  前言

上节我们看了下 ThreadLocal 的实现原理,这节我们来看下 InheritableThreadLocal 是用来干什么的呢?

我们首先看个简单的现象:

那我们把 ThreadLocal  换成 InheritableThreadLocal 的再来看下呢:

可以看到我们开辟的新线程里也能获取到了,为什么呢?接下来我们分析下。

2  源码分析

在看源码前我们先来看下 InheritableThreadLocal 类里的东西,其实很少我们看下:

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

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

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

可以看到就这么点东西,那他是怎么实现的呢?

2.1  getMap

我们看到 getMap 方法返回的是线程中的 inheritableThreadLocals 变量,我们看下:

可以看到其实就是创建 map 和 getMap 的时候初始化的是线程中的 inheritableThreadLocals 变量。

那么就有点邪门了,main 线程就是 set的时候,设置的也是设置进自己的 main线程里了啊,那么新线程 get 的时候也是从自己的线程中获取,它又怎么能拿到 main线程中的呢?是不是有这样的困惑?那是不是就是创建新线程的时候 jdk 给我动了什么手脚呢?我们看下新线程的创建:

2.2  创建线程过程

// 创建线程
public Thread(Runnable target) {
    // 调用重载方法
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
// Thread
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    // 继续调用重载 看最后一个参数是 默认是true
    init(g, target, name, stackSize, null, true);
}
// Thread 最后是这里来创建的
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }
    this.name = name;
    // 当前线程是谁? 是不是就是我们的 main线程
    Thread parent = currentThread();
    ...省略部分代码
    setPriority(priority);
    /**
     * 看这里 inheritThreadLocals 是不是默认为true
     * parent.inheritableThreadLocals 是不是不为空
     * 我们在创建线程前是不是 set了一个值 是不是就是放进了main线程的 inheritableThreadLocals 参数里
     * 这里是不是条件成立
     */
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        // 是不是就给当前要创建的新线程里,把 main 线程里的值初始化了一份进去 这就是新线程里为什么能拿到
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;
    /* Set thread ID */
    tid = nextThreadID();
}

这就是 InheritableThreadLocal,就是在当前创建的新线程里,判断父线程里的 inheritableThreadLocals 是不是为空,不为空的话就会复制一份进来。

2.3  createInheritedMap 

我们再来看下新线程是如何复制父线程中的:

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}
// ThreadLocalMap
private ThreadLocalMap(ThreadLocalMap parentMap) {
    // 父线程中的ThreadLocalMap 的 table
    Entry[] parentTable = parentMap.table;
    // 父线程中的ThreadLocalMap 的 table 的长度
    int len = parentTable.length;
    // 设置当前新 ThreadLocalMap 的 长度
    setThreshold(len);
    // 创建新数组
    table = new Entry[len];
    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 把 key、value 扔进去
                Entry c = new Entry(key, value);
                int h = key.threadLocalHashCode & (len - 1);
                while (table[h] != null)
                    h = nextIndex(h, len);
                // 设置回新的 ThreadLocalMap
                table[h] = c;
                size++;
            }
        }
    }
}

可以看到会创建一个新的 ThreadLocalMap 对象,然后把父亲的 ThreadLocalMap 遍历,进行新 Entry的 key、value的初始化,然后赋值给新的 ThreadLocalMap 。

2.3  带来的疑问

就是当我们是值类型传递的时候,是不是当 main线程更新了值,我们的新线程里是不是还是旧值的?我们看看:

引用类型的话,因为值地址传递,都是引用的同一个对象还好:

可以看到引用传递的话父子是可以同步的,值类型的话只会在创建新线程的那一刻和父线程保持一致,后续的更改不会作用到子线程里。所以大家使用的时候注意下。

3  小结

这节我们看了下 InheritableThreadLocal的实现的一个小原理,有理解不对的地方欢迎指正哈。

标签:InheritableThreadLocal,ThreadLocalMap,创建,inheritableThreadLocals,线程,原理,机制,null
From: https://www.cnblogs.com/kukuxjx/p/17230605.html

相关文章

  • Promise原理、方法及手写
    Promise原理、方法及手写ES6Promise对象:Promise对象-ECMAScript6入门(ruanyifeng.com)Promise/A+链接:Promises/A+(promisesaplus.com)什么是Promise?Promise是异......
  • JAVA-反射机制-2023-03-18
    反射机制,反过来,通过修改配置文件达到更新,拓展程序的目的,而不需要修改代码传统方法:对象.方法()反射机制:方法.invoke(对象)实体类及方法packagecom.feijian;publicc......
  • 对并发熟悉吗?说说synchronized及实现原理
    synchronized的基本使用synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。作用主要有三个:确保线程互斥的访问同步代码保证共享变量的修改能......
  • LVS+keepalived配置高可用架构和负载均衡机制(2)
    一、概述接上文,实际生产场景中,往往存在硬件资源数量的限制,此时需要设置DS节点复用RS节点。所以往往最常见的架构如下图所示:3台主机组建真实服务器集群,即3个RS2个RS兼......
  • 【视频】随机波动率SV模型原理和Python对标普SP500股票指数预测|数据分享|附代码数据
    全文链接:http://tecdat.cn/?p=22546 最近我们被客户要求撰写关于随机波动率SV模型的研究报告,包括一些图形和统计输出。什么是随机波动率?随机波动率(SV)是指资产价格的......
  • 说一下线程池内部工作原理(ThreadPoolExecutor)
    ThreadPoolExecutor构造方法的参数corePoolSize:线程池的核心线程数,说白了就是,即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。maximumPoolSize:最大......
  • 请你详细说说类加载流程,类加载机制及自定义类加载器
    当程序使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、链接、初始化三个步骤对该类进行类加载。加载类加载指的是将类的class文件读入内存,并为之创建一个java.......
  • clickhouse的数据存储原理
    ClickHouse是一个列式存储数据库,它的数据存储原理与传统的行式存储数据库有很大不同。以下是ClickHouse数据存储原理的一些关键点:列式存储:与行式存储数据库将数据按行存......
  • RPC原理简析
    RPC作用是什么?通过动态代理和反射透明友好地调用远程服务器。即调用过程跟本地调用服务一致,让构建分布式应用、微服务更简单。为什么要用RPC?过去的Java应用一般采用Contro......
  • HTTPS原理解析
    我们用https的目的是什么?为了A端与B端互发的消息就算被拦截获取到也是加密了无法查看的,通用的加密/解密过程如下:以上的过程分析如下:1A端传入加密串"xx"进A端的加密方......