ThreadLoacl

2023-11-09

三. ThreadLoacl 基础

  1. 在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。 但在有些情况下,synchronized不能保证多线程对共享变量的正确读写。例如类有一个类变量,该类变量会被多个类方法读写,当多线程操作该类的实例对象时,如果线程对类变量有读取、写入操作就会发生类变量读写错误,即便是在类方法前加上synchronized也无效,因为同一个线程在两次调用方法之间时锁是被释放的,这时其它线程可以访问对象的类方法,读取或修改类变量。 这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象,ThreadLocal的作用则恰恰是为了实现线程的不共享,即实现线程所所保留的副本各不冲突的一个作用,(可以理解为是为线程提供一个线程私有的变量副本,这样多个线程都可以随意更改自己线程局部的变量,不会影响到其他线程)从而实现并发的另类的一种解决和实现思路,但是ThreadLocal如果不谨慎使用,导致强引用等的关系出现,使其线程始终没被回收,则也会出现内存溢出等的现象

  2. ThreadlLocal 底层通过内部类ThreadLocalMap,实现,会将当前线程Thread作为Key,数据作为值进行存储,ThreadLocalMap中还有一个内部类Entry

  3. ThreadLocal 注意点:

  1. JVM利用设置ThreadLocalMap的Key为弱引用,来避免内存泄露。
  2. JVM利用调用remove、get、set方法的时候,回收弱引用。
  3. ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链,造成内存泄漏
  4. 当使用static ThreadLocal的时候,延长ThreadLocal的生命周期,那也可能导致内存泄漏。因为,static变量在类未加载的时候,它就已经加载,当线程结束的时候,static变量不一定会回收。那么,比起普通成员变量使用的时候才加载,static的生命周期加长将更容易导致内存泄漏危机。
  1. 么如何有效的避免ThreadLocalMap中的key也就是ThreadLocal被垃圾回收为null时造成的内存泄露

a. 在使用完毕后手动调用ThreadLocal的remove方法手动进行释放

  1. 使用示例
  • 存有需要设置线程私有变量的类Res ,该类中有一个计数变量,每次执行累计加1线程私有threadLocal
class Res {
    private Integer count;
    
    /*使用Threadlocal创建一个属于每个线程的变量
    * 只要执行这段代码的线程都会拥有这个变量,变量名就可以看做是Threadlocal对象名
    * 并且每个线程修改这个变量时,修改的是各自的副本,不影响其他线程的使用
    * 创建一个Thradelocal的对象,根据变量的类型,设置Threallocal的泛型类型
    * (变量可以是任何类型,一般为基本类型,或包装类型)对象后设置返回这个变量初始化值的方法
    * (也可以看为是声明一个属于每个线程的变量,初始化为0 */
   public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
      //返回该线程局部变量初始化值
      protected Integer initialValue() {
         return 0;
      };
   };
   
   //调用该方法,设置线程的局部变量累计+1,然后返回(先获取局部变量+1后再设置到局部变量里)
   public Integer getNum(){
        //获当前线程中局部变量值 然后+1
       Integer count= threadLocal.get()+1;
       //把+1后的count在设置给属于每个线程的变量
        threadLocal.set(count);
        //再次获取修改后的属于每个线程的变量
       return  threadLocal.get();
    }

	public void remove(ThreadLocal<Integer> threadLocal){
		//删除线程中的私有变量
		threadLocal.remove();
	}
  • 调用测试
public class ThreadLocaDemo2 extends Thread {
    private Res res;
    //初始化线程对象需要Res对象,因为在线程对象中
    //的run方法中要调用Res的方法,进而对Res中的某个
    //方法实现多线程
    public ThreadLocaDemo2(Res res) {
        this.res = res;
    }
    @Override//run方法中调用Res中的getNum方法
    public void run() {
        //循环调用getNum方法(getNum方法每执行一次,对执行这个方法的线程
        // 的局部变量+1,然后返回调用这个方法的线程的局部变量)
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + res.getNum());
        }
    }
    public static void main(String[] args) {
        //创建Res对象,将res对象传入ThreadLocaDemo2中,创建自定义线程类对象
        Res res = new Res();
        ThreadLocaDemo2 threadLocaDemo1 = new ThreadLocaDemo2(res);//线程1
        ThreadLocaDemo2 threadLocaDemo2 = new ThreadLocaDemo2(res);//线程2
        ThreadLocaDemo2 threadLocaDemo3 = new ThreadLocaDemo2(res);//线程3
        //运行线程1.会自动运行run方法,循环调用res中的getNum方法,
        // 获取到线程1的局部变量
        threadLocaDemo1.start();
        //运行线程2.会自动运行run方法,循环调用res中的getNum方法,
        // 获取到线程2的局部变量
        threadLocaDemo2.start();
      /*查看运行结果,会发现,虽然线程1在运行后循环调用了getNum
      * 最终把线程1的变量修改为了3,
      * 当线程2在去运行,循环调用getNum时,局部变量还是原来的0开始
      * 线程1的修改不会影响到线程2*/

	
    }
}

二. InheritableThreadLocal

  1. 在某些时刻需要考虑ThreadLocal的传递问题,使用InheritableThreadLocal
  2. Thread类中包含 threadLocals 和 inheritableThreadLocals 两个变量,其中 inheritableThreadLocals 即主要存储可自动向子线程中传递的ThreadLocal.ThreadLocalMap
public class Thread implements Runnable {
   //......(其他源码)
     
    //当前线程的ThreadLocalMap,主要存储该线程自身的ThreadLocal
    ThreadLocal.ThreadLocalMap threadLocals = null;

    
    //InheritableThreadLocal,自父线程集成而来的ThreadLocalMap,
    //主要用于父子线程间ThreadLocal变量的传递
    //本文主要讨论的就是这个ThreadLocalMap
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    //......(其他源码)
}
  1. InheritableThreadLocal类重写了ThreadLocal的3个方法
   
    //该函数在父线程创建子线程,向子线程复制InheritableThreadLocal变量时使用
    protected T childValue(T parentValue) {
        return parentValue;
    }

     //由于重写了getMap,操作InheritableThreadLocal时,
     //将只影响Thread类中的inheritableThreadLocals变量,
     //与threadLocals变量不再有关系
     ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

	  
    //类似于getMap,操作InheritableThreadLocal时,
    //将只影响Thread类中的inheritableThreadLocals变量,
    //与threadLocals变量不再有关系
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
  1. Thread初始化可以看到,采用默认方式产生子线程时,inheritThreadLocals=true;若此时父线程inheritableThreadLocals不为空,则将父线程inheritableThreadLocals传递至子线程
	//默认情况下,设置inheritThreadLocals可传递
	private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
	
	
    //初始化一个线程.此函数有两处调用,
    //1、上面的 init(),不传AccessControlContext,inheritThreadLocals=true
    //2、传递AccessControlContext,inheritThreadLocals=false
	private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        //......(其他代码)

        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

        //......(其他代码)
    }
  1. ThreadLocal.createInheritedMap中的createInheritedMap
    static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

	//构建一个包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
    //该函数只被 createInheritedMap() 调用.
    private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            // ThreadLocalMap 使用 Entry[] table 存储ThreadLocal
            table = new Entry[len];

            // 逐一复制 parentMap 的记录
            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) {
                        // 可能会有同学好奇此处为何使用childValue,而不是直接赋值,
                        // 毕竟childValue内部也是直接将e.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++;
                    }
                }
            }
  1. InheritableThreadLocal主要用于子线程创建时,需要自动继承父线程的ThreadLocal变量,方便必要信息的进一步传递

三. TransmittableThreadLocal

  1. TransmittableThreadLocal 是Alibaba开源的、用于解决 “在使用线程池等会缓存线程的组件情况下传递ThreadLocal” 问题的 InheritableThreadLocal 扩展。若希望 TransmittableThreadLocal 在线程池与主线程间传递,需配合 TtlRunnable 和 TtlCallable 使用。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreadLoacl 的相关文章

随机推荐

  • C++函数基础

    一 函数的定义和使用 1 函数的定义 类型说明符 函数名 含类型说明的形参表 语句序列 如 int GetSum int a int b return a b 2 形式参数 形式参数的作用是实现主函数与被调函数之间的联系 3 函数的返回值和
  • 【ubuntu】ubuntu实体机与windows互传文件(两台电脑)

    先记录一些命令 dpkg list 查看软件列表 sudo apt get purge remove 包名 purge是可选项 写上这个属性是将软件及其配置文件一并删除 如不需要删除配置文件 可执行sudo apt get remove 包
  • Python列表操作中extend和append的区别

    1 用法 append 用于在列表末尾添加新的对象 输入参数为对象 extend 用于在列表末尾追加另一个序列中的多个值 输入对象为元素队列 2 相同点 两个都是对列表即list进行的操作 具体句法可以写为 list1 append obj
  • 解决EXPLORER应用程序错误,桌面出不来

    打开运行 输入CMD 输入for 1 in windir system32 dll do regsvr32 exe s 1 意思是注册所有DLL组件 一般都能解决问题 转载于 https blog 51cto com feifei888 4
  • word文件doc、docx转pdf

    综合类管理系统不管是自研还是外包项目都会被客户或者产品经理要求 实现word导出 excel导出 pdf导出等功能 其实pdf导出呢 有很多种方式 我实现过的就有两种 接下来呢 就说说其中的一种 就是当你已经实现了word导出 或有明确的要
  • 粉丝文化:抖音广告短视频美妆营销中,男明星比女明星更带货?

    1996年 木村拓哉为佳丽宝拍摄了一支口红广告 这条广告轰动一时 代言的口红两个月就卖出了300万支 从此 男明星就成了美妆品牌的宠儿 众多美妆品牌开始启用男明星代言人 男明星为何有如此强力的带货潜力 美妆品牌如何在短视频时代占得先机 抖音
  • 阿里天池比赛——街景字符编码识别

    文章目录 前言 一 街景字符编码识别 1 目标 2 数据集 3 指标 总结 前言 之前参加阿里天池比赛 好久了 一直没有时间整理 现在临近毕业 趁论文外审期间 赶紧把东西整理了 5月底学校就要让我们滚蛋了 哭哭哭 大运会的牺牲品 一 街景字
  • 赛马游戏的java设计_赛马游戏源码

    0 Intro pos g setClip 10 10 HorseMidlet imgIntro 0 getWidth HorseMidlet i mgIntro 0 getHeight g drawImage HorseMidlet im
  • 面试题记1

    希望各位看客们能积极提供答案 1 125874和它的两倍251748 包含着同样的数字 只是顺序不同 找出最小的正整数x 使得2x 3x 4x 5x 和6x都包含有相同的数字 2 求100 各位数之和 3 是用从1到9所有数字 将其任意的连
  • Notion?Roam?OneNote? 不要再用这些垃圾做笔记啦

    双向链接 最近因为Roam Research 双向链接在笔记圈子里火了起来 Notion也在准备做了 那么双向链接是什么呢 我用我的我关于管道的一则笔记给大家讲明白 管道的实现 Linux里 管道实现的原理是 Shell进程先调用pipe创
  • 浅谈 qmake 之 shadow build

    shadow build shadow build 是什么东西 就是将源码路径和构建路径分开 也就是生成的makefile文件和其他产物都不放到源码路径 以此来保证源码路径的清洁 这不是qmake独创的东西 cmake中早就使用这个东西了
  • 性能测试_Day_10(负载测试-获得最大可接受用户并发数)

    目录 如何理解负载测试 如何实现负载测试 jpgc Standard Set插件安装 jpgc Standard Set使用方法 负载测试分析指标 获得最大可接受用户并发数 区间值 负载测试分析指标 获得最大可接受用户并发数 真实值 负载测
  • 阅读论文《Deep Bilateral Learning for Real-Time Image Enhancement》

    这是2017 siggraph的一篇论文 寒假boss让我看这篇论文我没怎么看懂 最近在公司实习 发现该论文的成果已经移到手机端上了 效果还非常不错 这里我重新温习了一下这篇论文 发现有许多可以借鉴的地方 是一篇非常不错的论文 这里重新叙述
  • 我碰到avs错误

    1 写好的avs脚本用播发器不能播放 并且报unexpected chatacter 错误 解决办法 1 尽管avs支持汉语文件路径 但是仍要确认标点符号是否为英文状态下 2 将AVS脚本用记事本打开 重新存为并把编码格式修改成ASNI格式
  • 数值计算方法python实现

    包括 泰勒级数展开 差分逼近微分 二分法求解 试位法求解 迭代法求根 牛顿法求根 正割法 贝尔斯托法多项式求跟 多项式回归 牛顿差商插值 拉格朗日插值法 三次样条插值法 二次样条插值法 高斯消元法 求解线性代数方程组 代码在我的github
  • 事件循环与线程 一

    初次读到这篇文章 译者感觉如沐春风 深刻体会到原文作者是花了很大功夫来写这篇文章的 文章深入浅出 相信仔细读完原文或下面译文的读者一定会有收获 由于原文很长 原文作者的行文思路是从事件循环逐渐延伸到线程使用的讨论 译者因时间受限 暂发表有关
  • SnowFlake 算法

    SnowFlake 算法 1 介绍 是 Twitter 开源的分布式 id 生成算法 核心思想 使用一个 64 bit 的 long 型的数字作为全局唯一 id 2 结构 0 0001000000 0000010000 0001000100
  • KVM架构与原理详解

    1 KVM架构 KVM 基本上有两个组件构成 1 kvm 驱动 现在已经是Linux内核的一个模块了 它的作用主要是负责虚拟机的创建 虚拟内存的分配 虚拟CPU寄存器的读写和虚拟cpu的运行 2 另一个组件是 Qemu QEMU是一个通用的
  • Wsl2 Ubuntu18.04图形化界面,亲测成功

    Wsl2 Ubuntu18 04图形化界面 亲测成功 Windows端 Linux端 最后 抖抖索索搞了两天 差点Windows系统都重装 终于搞成功了 参考文献 一定要看 非常感谢这个哥们 成功搞出来了 Windows端 powershe
  • ThreadLoacl

    目录 三 ThreadLoacl 基础 二 InheritableThreadLocal 三 TransmittableThreadLocal 三 ThreadLoacl 基础 在Java的多线程编程中 为保证多个线程对共享变量的安全访问