IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 较真儿学源码系列-InheritableThreadLocal(逐行源码带你分析作者思路) -> 正文阅读

[Java知识库]较真儿学源码系列-InheritableThreadLocal(逐行源码带你分析作者思路)

????????Java版本:8u261。

????????之前我写过对ThreadLocal源码进行分析的文章,感兴趣的话可以查看《较真儿学源码系列-ThreadLocal(逐行源码带你分析作者思路)》???????。


????????InheritableThreadLocal是ThreadLocal的子类,通过前面的分析可知,ThreadLocal只能在同一线程内进行变量的共享,而InheritableThreadLocal不仅可以在同一线程内进行变量的共享,而且可以在父子线程之间进行变量的共享。比如说在父线程a中创建了一个子线程b,那么在a线程中用InheritableThreadLocal包装的变量,在子线程b中也能获取的到。但需要注意的是:InheritableThreadLocal和ThreadLocal一样,在同级线程中依然不能共享变量的值。并且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);
    }
}

????????以上便是InheritableThreadLocal的全部源码,可见其只是覆写了ThreadLocal的三个方法而已。而在getMap和createMap方法中可以看到inheritableThreadLocals这个属性,那么这个属性到底是干什么的呢?其实它跟threadLocals属性一样,都是放在Thread类中的属性:

public class Thread implements Runnable {
    //...

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    //...
}

????????正是因为有了inheritableThreadLocals这个属性,就可以让子线程来访问父线程中的本地变量。

????????在创建线程的时候,会调用到Thread类的init方法:

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

    Thread parent = currentThread();
    //...
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        /*
        将父线程中inheritableThreadLocals的数据初始化到一个新的ThreadLocalMap中,
        并赋值给子线程的inheritableThreadLocals
         */
        this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    //...
}

????????这里省略了其他不相干逻辑,只需要来看一下inheritableThreadLocals的初始化过程,进一步跟踪第17行代码处:

/**
 * ThreadLocal:
 */
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    //parentMap是父线程中的数据
    return new ThreadLocalMap(parentMap);
}

/**
 * 将父线程中的inheritableThreadLocals复制到一个新的ThreadLocalMap中
 * 个人认为这里直接将parentMap返回回去应该也是可以的,但这里重新构建一个
 * ThreadLocalMap感觉是为了做一遍清理工作,将Entry为null的哈希槽清理掉
 */
private ThreadLocalMap(ThreadLocalMap parentMap) {
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    //设置子线程的threshold
    setThreshold(len);
    //初始化子线程的table(因为是在子线程创建的时候调用到这里的,所以不需要判断是否已经初始化,这里一定是未初始化的)
    table = new Entry[len];

    //遍历父线程中的table
    for (int j = 0; j < len; j++) {
        //获取其中的Entry
        Entry e = parentTable[j];
        if (e != null) {
            //获取当前槽中的Entry中保存的ThreadLocal
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                //这里会调用InheritableThreadLocal覆写的childValue方法,也就是返回e.value本身
                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;
                //计数+1
                size++;
            }
        }
    }
}

/**
 * InheritableThreadLocal:
 * 第32行代码处:
 */
protected T childValue(T parentValue) {
    return parentValue;
}

????????完整的流程如下:

  1. 首先父线程调用set或get方法时,会调用InheritableThreadLocal覆写的getMap方法返回inheritableThreadLocals,但因为其没有初始化,所以会调用覆写的createMap方法来创建ThreadLocalMap,并赋值给inheritableThreadLocals。这样父线程的inheritableThreadLocals属性就不为null了;
  2. 接着父线程会调用set方法往父线程的inheritableThreadLocals属性中的table数组中进行赋值;
  3. 然后创建子线程的时候,会调用Thread类的init方法中的ThreadLocal.createInheritedMap方法。将父线程inheritableThreadLocals属性中的数据初始化到一个新的ThreadLocalMap中,并同时赋值给子线程的inheritableThreadLocals。这样子线程的inheritableThreadLocals属性中就有了父线程中的数据;
  4. 最后子线程在调用get方法的时候就能拿到父线程中的数据了。但是需要注意的是:子线程执行完毕后,父线程此时调用get方法拿到的还是之前父线程中的inheritableThreadLocals,并不是子线程会往其中更改过的ThreadLocalMap。也就是说子线程的数据不会传递给父线程,子线程只有在一开始初始化的时候才会同步父线程中的数据。

原创不易,未得准许,请勿转载,翻版必究

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-06 17:11:36  更:2022-06-06 17:15:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:26:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码