ThreadLocal笔记

news2024/9/22 7:29:53

并发的场景中,如果有多个线程同时修改公共变量,可能会出现线程安全问题,即该变量最终结果可能出现异常。
如果使用锁来保证资源隔离,会存在大量锁等待,会让响应时间延长很多。
ThreadLocal的核心思想是:共享变量在每个线程都有一个副本,每个线程操作的都是自己的副本,对另外的线程没有影响。

ThreadLoacl原理

public class ThreadLocal<T> {
     ...
     public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取成员变量ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //根据threadLocal对象从map中获取Entry对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                //获取保存的数据
                T result = (T)e.value;
                return result;
            }
        }
        //初始化数据
        return setInitialValue();
    }
    
    private T setInitialValue() {
        //获取要初始化的数据
        T value = initialValue();
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取成员变量ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //如果map不为空
        if (map != null)
            //将初始值设置到map中,key是this,即threadLocal对象,value是初始值
            map.set(this, value);
        else
           //如果map为空,则需要创建新的map对象
            createMap(t, value);
        return value;
    }
    
    public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取成员变量ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //如果map不为空
        if (map != null)
            //将值设置到map中,key是this,即threadLocal对象,value是传入的value值
            map.set(this, value);
        else
           //如果map为空,则需要创建新的map对象
            createMap(t, value);
    }
    
     static class ThreadLocalMap {
        ...
     }
     ...
}

ThreadLocal的get方法、set方法和setInitialValue方法,其实最终操作的都是ThreadLocalMap类中的数据。

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
   }
   ...
   private Entry[] table;
   ...
}

ThreadLocalMap里面包含一个静态的内部类Entry,该类继承于WeakReference类,说明Entry是一个弱引用。

ThreadLocalMap内部还包含了一个Entry数组,其中:Entry = ThreadLocal + value。

而ThreadLocalMap被定义成了Thread类的成员变量。

public class Thread implements Runnable {
    ...
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

在这里插入图片描述

Entry是由threadLocal和value组成,其中threadLocal对象是弱引用,在GC的时候,会被自动回收。

ThreadLocalMap的Entry为什么用ThreadLocal做key?

如果一个线程只使用一个ThreadLocal对象,使用Thread做key是可以的。但是一个线程中不可能只使用一个ThreadLocal对象,再使用Thread做key就有问题

Entry的key为什么设计成弱引用?

弱引用的对象,在GC做垃圾清理的时候,就会被自动回收了。

如果key是弱引用,当ThreadLocal变量指向null之后,在GC做垃圾清理的时候,key会被自动回收,其值也被设置成null。

线程很多情况下是复用的,一个线程执行多个任务。一个任务执行完如果不把key设置为nul会一直存在,从而造成内存泄漏。

Entry的value为什么不设计成弱引用?

Entry的value假如只是被Entry引用,有可能没被其他地方引用。如果将value改成了弱引用,被GC贸然回收了(数据突然没了),可能会导致出现异常。

ThreadLocal真的会导致内存泄露?

强引用链:Thread变量 -> Thread对象 -> ThreadLocalMap -> Entry -> value -> Object

若Thread为工作线程,长期存在,会导致内存泄漏

如何解决内存泄露问题?

在finally代码块中,调用remove方法清理没用的数据,remove方法中会把Entry中的key和value都设置成null,这样就能被GC及时回收,无需触发额外的清理机制,所以它能解决内存泄露问题。

父子线程如何共享数据?

ThreadLocal都是在一个线程中保存和获取数据的。

 @Test
    public void test1() {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        threadLocal.set(6);
        System.out.println("父线程获取数据:" + threadLocal.get());

        new Thread(() -> {
            System.out.println("子线程获取数据:" + threadLocal.get());
        }).start();
    }
    

在这里插入图片描述
使用InheritableThreadLocal,它是JDK自带的类,继承了ThreadLocal类。

  @Test
    public void test2(){
        InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
        threadLocal.set(6);
        System.out.println("父线程获取数据:" + threadLocal.get());

        new Thread(() -> {
            System.out.println("子线程获取数据:" + threadLocal.get());
        }).start();
    }

在这里插入图片描述
InheritableThreadLocal的init方法中会将父线程中往ThreadLocal设置的值,拷贝一份到子线程中。

线程池中如何共享数据?

   public void test3(){
        InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
        threadLocal.set(6);
        System.out.println("父线程获取数据:" + threadLocal.get());

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        threadLocal.set(6);
        executorService.submit(() -> {
            System.out.println("第一次从线程池中获取数据:" + threadLocal.get());
        });

        threadLocal.set(7);
        executorService.submit(() -> {
            System.out.println("第二次从线程池中获取数据:" + threadLocal.get());
        });
    }

在这里插入图片描述

由于这个例子中使用了单例线程池,固定线程数是1。

第一次submit任务的时候,该线程池会自动创建一个线程。因为使用了InheritableThreadLocal,所以创建线程时,会调用它的init方法,将父线程中的inheritableThreadLocals数据复制到子线程中。在主线程中将数据设置成6,第一次从线程池中获取了正确的数据6。

之后,在主线程中又将数据改成7,但在第二次从线程池中获取数据却依然是6。

因为第二次submit任务的时候,线程池中已经有一个线程了,就直接拿过来复用,不会再重新创建线程了。所以不会再调用线程的init方法,所以第二次其实没有获取到最新的数据7,还是获取的老数据6。

使用阿里巴巴的一个开源jar包

   <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.14.2</version>
        </dependency>
@Test
    public void test4(){
        TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();
        threadLocal.set(6);
        System.out.println("父线程获取数据:" + threadLocal.get());

        ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));

        threadLocal.set(6);
        ttlExecutorService.submit(() -> {
            System.out.println("第一次从线程池中获取数据:" + threadLocal.get());
        });

        threadLocal.set(7);
        ttlExecutorService.submit(() -> {
            System.out.println("第二次从线程池中获取数据:" + threadLocal.get());
        });
    }

在这里插入图片描述

ThreadLocal为什么建议用static修饰?

static修饰的变量是在类在加载时就分配地址了,在类卸载才会被回收

如果变量ThreadLocal是非static的就会造成每次生成实例都要生成不同的ThreadLocal对象,虽然这样程序不会有什么异常,但是会浪费内存资源

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云服务器centos8搭建网站 apache+php+mysql

由于对数据库容量要求比较大&#xff0c;年费用300左右的普通虚拟主机只能提供500M-1G的数据库&#xff0c;不能满足要求&#xff0c;故寻找到同样费用的云服务器单核、1G内存、系统盘50G&#xff0c;缺点是只提供基本系统centos&#xff0c;其他要自己搭建&#xff0c;经过一周…

05_openstack之Neutron网络管理

目录 一、环境准备 二、通过Horizon设置外部网络 1、创建外网网络 2、创建内网网络 3、创建路由 一、环境准备 部署openstack私有云环境&#xff1a;02_openstack私有云部署_桂安俊kylinOS的博客-CSDN博客 创建项目和用户&#xff1a;03_openstack之项目及用户管理_桂安…

fastTEXT论文解读并附实例代码

上一篇博文是入门使用级别&#xff0c;但对于面试来说则不够&#xff0c;毕竟领导一问三不知必定over&#xff0c;其基本原理还是要搞清楚&#xff0c;因而有此博文。paper在此 0&#xff0c;绪论 考虑紧致特征以减少存储空间&#xff0c;提出在PQ&#xff08; product quant…

三、CANdelaStudio入门-视图类型(View type)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的各个视图类型:Standard View、Expert V…

Jmeter的使用教程(安装)

前言&#xff1a;之前在工作中未接触过Jmeter&#xff0c;只是知道这个Java语言开发的工具主要是测试接口的&#xff0c;还能做一些性能和压力的测试、并发什么的。目前市面上的有postman、apifox&#xff0c;request、swagger。 一、下载&#xff1a; 1、可以直接到官网下载&a…

(Cascade extended state observer)级联ADRC的simulink仿真和程序---送给中国研究学者的精华版

在这里先声明一下&#xff0c;级联CESO由美国学者Rafal Madonski的论文 《Cascade extended state observer for active disturbance rejection control applications under measurement noise》提出&#xff0c;本人只是将他给的模型给中国学者研究&#xff0c; 模型适用于各…

做了几年“斜杠青年”,我在ZStack立志做国产云计算的研发

在浅黑科技《ZStack&#xff1a;这群做云的人有点“轴”》一文中&#xff0c;作者史中提到&#xff0c;这是一篇国产云计算佼佼者ZStack的创业史&#xff0c;文中记录了因为热爱而聚集起来的最早一批ZStacker&#xff0c;他们生活没有退路&#xff0c;但热爱未有止息。 实际上…

概率论与数理统计_第1章_几何概型

1 定义 若一个试验具有下列两个特征&#xff1a; &#xff08;1&#xff09;试验的所有可能结果是无限多个&#xff0c; 且全体结果可以用一个有度量的几何区域 Ω 来表示&#xff1b; &#xff08;2&#xff09;每个可能结果 都相同概率可能发生&#xff0c; 则该试验称为几何…

Pytorch:Torch数据类型学习整理与记录

文章目录前言一、Tensor数据类型简介Tensor数据类型是什么?Tensor数据类型有哪些指定调用的API生成相关数据类型dtype属性指定Tensor内置的简单数据类型二、Tensor数据类型的基本使用Tensor初始化基于list列表和nparrayTensor相关API基于指定Tensor类型进行初始化基于Randn生成…

postgres源码解析40 表创建执行全流程梳理--4

本文讲解非系统表的创建逻辑&#xff08;[<fontcolor0000dd>普通表和索引表]&#xff09;&#xff0c;其入口函数为heap_create&#xff0c;内部公共接口函数为RelationBuildLocalRelation和RelationCreateStorage相关知识回顾见&#xff1a; postgres源码解析38 表创建执…

Mac M1使用UTM安装centos7 x86_64虚拟机

一、环境说明 1. 宿主机环境 macbook m1 pro 16G 2. UTM版本 UTM是基于QEMU的系统模拟器和虚拟机主机&#xff0c;适用于iOS和macOS。 UTM is a full featured system emulator and virtual machine host for iOS and macOS. It is based off of QEMU. 最新版下载地址&…

带你玩转序列模型之NLP与词嵌入(二)

目录 一.Word2Vec 二.负采样 三.GloVe词向量 四.情绪分类 五.词嵌入除偏 一.Word2Vec 在上个视频中你已经见到了如何学习一个神经语言模型来得到更好的词嵌入&#xff0c;在本视频中你会见到 Word2Vec算法&#xff0c;这是一种简单而且计算时更加高效的方式来学习这种类…

用于 Python 降维的主成分分析

减少预测模型的输入变量数称为降维。 较少的输入变量可以产生更简单的预测模型&#xff0c;该模型在对新数据进行预测时可能具有更好的性能。 也许机器学习中最流行的降维技术是主成分分析&#xff0c;简称PCA。这是一种来自线性代数领域的技术&#xff0c;可用作数据准备技术…

耗时大半个月收整全套「Java架构进阶pdf」

花了我大半个月时间收整了全套的「Java架构进阶pdf」&#xff0c;这一波下来&#xff0c;刷完你就会知道&#xff0c;真真香啊&#xff0c;我的心血果然&#xff0c;没白费&#xff01; 请注意&#xff1a;关于全套的「Java架构进阶pdf」&#xff0c;我会从面试-筑基-框架-分布…

【Android App】实战项目之仿微信的视频通话(附源码和演示 超详细必看)

需要源码请点赞关注收藏后评论区留言私信~~~ 虽然手机出现许多年了&#xff0c;它具备的功能也越来越丰富&#xff0c;但是最基本的通话功能几乎没有变化。从前使用固定电话的时候&#xff0c;通话就是听声音&#xff1b;如今使用最新的智能手机&#xff0c;通话仍旧是听声音。…

无刷三相直流电机电动工具驱动方案设计

电动工具是一种工具&#xff0c;其致动通过附加的动力源和机构比仅其他手工劳动与使用手工工具&#xff0c;电动工具用于工业、建筑、花园、做饭、清洁等家务劳动&#xff0c;以及在房子周围用于驱动&#xff08;紧固件&#xff09;、钻孔、切割、成型、打磨、研磨、布线、抛光…

【微服务】Java agent 使用详解

一、前言 于一个即将上线的应用来说&#xff0c;系统监控是必不可少的&#xff0c;为什么需要监控呢&#xff1f;应用是跑在服务器上的&#xff0c;应用在运行过程中会发生各自意想不到的问题&#xff0c;像大家熟知的OOM&#xff0c;mysql故障&#xff0c;服务器宕机&#xff…

cubeIDE开发,基于已有的STM32CubeMX (.ioc)创建工程文件

一、STM32Cube 生态系统 可以在其官网查看&#xff0c;支持中文。 STM32Cube - Discover the STM32Cube Ecosystem - STMicroelectronics ​ 截取官网的STM32Cube家族的软件工具描述&#xff1a; 【1】STM32CubeMX, 面向任意STM32设备的配置工具。这款简单易用的图形用户界面为…

ubuntu根目录清理

0.防范于未然&#xff08;就像给window电脑清理垃圾&#xff09; 清理ubuntu用不上的东西的常用命令 # 系统自带清理命令 sudo apt-get autoclean sudo apt-get clean sudo apt-get autoremove# 查看目录占用空间 sudo du -cks * | sort -rn | head -10 sudo du --max-depth1…

[附源码]计算机毕业设计JAVA血库管理系统

[附源码]计算机毕业设计JAVA血库管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Ma…