ThreadLocal学习笔记

TwinCAT 3 实战:手把手教你用PLC和C++读写第三方伺服驱动器的CoE参数 本文详细介绍了在TwinCAT 3环境中通过PLC和C++两种技术路线实现对第三方伺服驱动器CoE参数的精准操控。从环境准备到具体实现,包括硬件配置、核心函数使用及性能优化策略,为工程师提供了全面的实战指南。特别针对CoE参数访问的效率和稳定性问题,给出了实用的调试技巧和异常处理方案。 阅读详情

ThreadLocal学习笔记

最近看一些面经,好像很多时候都有提到ThrealLocal的实现原理,之前在学习多线程的时候似乎没有太注意ThreadLocal这个本地线程,现在看看源码,学习一下。

ThreadLocal的实现原理

ThreadLocal是线程内部的数据存储类,通过它可以指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取数据.

自定义ThreadLocal实现

自定义实现一个ThreadLocal,先定义一个map,将当前线程作为key,线程拥有的数据作为值存入map,实现value为当前线程私有。


/**
 * @ClassName MyThreadLocal
 * @Description: 自定义一个简易ThreadLocal
 * @Author zhouyang
 * @Date 2019/5/22 下午9:55.
 */

public class MyThreadLocal<T> {

    //基于map实现
    private Map<Thread,T> values = new HashMap<>();

    //存值
    public synchronized void set(T value){
        values.put(Thread.currentThread(),value);
    }

    //取值
    public synchronized T get(){
        return values.get(Thread.currentThread());
    }
}

自定义可以完成基本功能,但是在多线程中性能较差,会发生冲突。

源码解读

源码中的set和get方法,保证了各个线程之间的数据互不干扰。


 public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

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

    

可以发现,每个线程中都有一个ThreadLocalMap数据结构,当执行set方法时,其值是保存在当前线程的threadLocals变量中,当执行set方法中,是从当前线程的threadLocals变量获取。
所以在线程1中set的值,对线程2来说是摸不到的,而且在线程2中重新set的话,也不会影响到线程1中的值,保证了线程之间不会相互干扰。

ThreadLoalMap

ThredLocalMap是ThreadLocal中的一个内部类,用于存储线程的ThreadLocalMap对象。


static class ThreadLocalMap {
        // 自定义Entry类用于存储<ThreadLocal, Value>键值对.
        static class Entry extends  WeakReference<ThreadLocal> {
            Object value;
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

        private Entry[] table;
        private static final int INITIAL_CAPACITY = 16;
        private int threshold;

        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            // 使用数组来模拟实现Map.
            table = new Entry[INITIAL_CAPACITY];
            // 使用ThreadLocal的HashCode来生成下标,尽量减少哈希碰撞
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        // 设置扩容resize时的阈值
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }
    }

在ThreadLoalMap中,也是初始化一个大小16的Entry数组,Entry对象用来保存每一个key-value键值对,只不过这里的key永远都是ThreadLocal对象,是不是很神奇,通过ThreadLocal对象的set方法,结果把ThreadLocal对象自己当做key,放进了ThreadLoalMap中。
这里需要注意的是,ThreadLoalMap的Entry是继承WeakReference,和HashMap很大的区别是,Entry中没有next字段,所以就不存在链表的情况了。

hash冲突

在ThreadLocalMap中,数据的存入源码:

 /**
       * Set the value associated with key.
       *
       * @param key the thread local object
       * @param value the value to be set
       */
      private void set(ThreadLocal<?> key, Object value) {

          // We don't use a fast path as with get() because it is at
          // least as common to use set() to create new entries as
          // it is to replace existing ones, in which case, a fast
          // path would fail more often than not.

          Entry[] tab = table;
          int len = tab.length;
          int i = key.threadLocalHashCode & (len-1);

          for (Entry e = tab[i];
               e != null;
               e = tab[i = nextIndex(i, len)]) {
              ThreadLocal<?> k = e.get();

              if (k == key) {
                  e.value = value;
                  return;
              }

              if (k == null) {
                  replaceStaleEntry(key, value, i);
                  return;
              }
          }

          tab[i] = new Entry(key, value);
          int sz = ++size;
          if (!cleanSomeSlots(i, sz) && sz >= threshold)
              rehash();
      }

在插入过程中,根据ThreadLocal对象的hash值,定位到table中的位置i,过程如下:

  • 1、如果当前位置是空的,那么正好,就初始化一个Entry对象放在位置i上;
  • 2、不巧,位置i已经有Entry对象了,如果这个Entry对象的key正好是即将设置的key,那么重新设置Entry中的value;
  • 3、很不巧,位置i的Entry对象,和即将设置的key没关系,那么只能找下一个空位置;
    这样的话,在get的时候,也会根据ThreadLocal对象的hash值,定位到table中的位置,然后判断该位置Entry对象中的key是否和get的key一致,如果不一致,就判断下一个位置
    可以发现,set和get如果冲突严重的话,效率很低,因为ThreadLoalMap是Thread的一个属性,所以即使在自己的代码中控制了设置的元素个数,但还是不能控制其它代码的行为。

内存泄露

如果使用ThreadLocal的set方法之后,没有显示的调用remove方法,就有可能发生内存泄露,所以养成良好的编程习惯十分重要,使用完ThreadLocal之后,记得调用remove方法。

ThreadLocal<String> localName = new ThreadLocal();
try {
    localName.set("crazyang");
    // 其它业务逻辑
} finally {
    localName.remove();
}

【ROS】ROS局域网下多机通讯方法 最近工作中需要用到多机通讯,这里稍微总结一下使用方法。一、网络配置ifconfig这里举个例子,主机的IP地址为192.168.1.127,从机的IP地址为192.168.1.126;之后可测试一下能否互相ping通,成功后进行下一步。 阅读详情

相关推荐

Arduino应用——PWM控制直流电机风扇

Arduino应用——PWM控制直流电机风扇PWM原理实验器件功能描述如何改变文本的样式插入链接与图片实现代码生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 最近在使用arduino UNO开发板完成课程小作业。其中一个要求设计一个电风扇控制系统,要求能够通过PWM调节风扇的转速。期间搜索了许多例程和方法,但受限于器件,效果不

hypecycle的博客 2万+

ThreadLocal ThreadContext 存储多个线程变量,跨类获取

ThreadLocal ThreadContext 存储多个线程变量,跨类获取

1817

用STM32F103C8T6和TB6612驱动模块,从零搭建一辆能避障循迹的智能小车(附完整代码)

本文详细介绍了如何使用STM32F103C8T6和TB6612驱动模块从零搭建一辆能避障循迹的智能小车。内容涵盖硬件选型、电路设计、底层驱动配置以及智能行为算法实现,并附有完整代码示例,帮助开发者快速掌握嵌入式开发的核心技术。

weixin_42641741的博客 445

在父线程中设置的ThreadLocal变量,怎样在子线程中获取?

首先我们看一个例子 public class TestThreadLocal { public static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main( String[] args ) { // 色后置线程变量 threadLocal.set("hello world"); Thread thread

半夏_2021的博客 3127

Halcon 3D视觉实战:5步将线激光高度数据转为可分析的深度图与点云

本文详细介绍了如何利用Halcon平台将线激光高度数据转换为深度图与点云的5步实战方法。通过具体代码示例和工业检测案例(如锂电池胶水测量),帮助工程师高效处理3D视觉数据,实现从原始数据到三维可视化的完整技术链路。

weixin_29039773的博客 361

解决2020R2,2022R1等版本的SCDM无法编辑草图尺寸的问题

解决2020R2,2022R1等版本的SCDM无法编辑草图尺寸的问题

y_u_yu_yu_的博客 1万+

【Java多线程】——ThreadLocal类的用法

    变量值的共享可以通过使用public static的形式,使得一个类的所有实例对象共享类中的同一个变量。但是如果想要实现每个线程都有自己的一个共享变量该如何解决呢?这个时候就要用到ThreadLocal类了。ThreadLocal类就是用来解决每个线程都能绑定自己的值,可以把ThreadLocal类看成一个全局的存放数据的盒子,盒子中可以存放每个线程自己的数据。一、使用ThreadLoca...

名侦探张六儿的博客 519

运放单电源供电和双电源供电的实际应用案例

文章摘要:本文对比分析了运放单电源与双电源供电的典型应用场景及电路设计要点。单电源供电适用于低成本、小体积设备,如便携音箱(5V供电+中点偏置)、工业4-20mA信号转换(12V供电+电压跟随)和物联网传感器(3.3V低功耗设计);双电源供电则适合高精度要求场景,如Hi-Fi音响(±15V对称输出)、医疗设备(±5V微弱信号放大)和精密仪器(±15V宽动态范围)。文末提供常见运放选型对照表,涵盖LM324、TL082等型号的供电参数与核心特性,为不同应用场景提供选型参考。(149字)

qq_37999850的博客 609

利用ThreadLocal实现不同方法的值获取

假设有这么个需求,用户登录系统以后,将用户的token存入浏览器的本地存储,每次发送请求时将用户token携带上,方便后台控制层或业务层获取,那么此时可以有什么解决方案? 利用拦截器获取请求头中的token,然后将token存入请求域中,然后将这个request传入controller,controller再传给service,其实这里也不用再将token存入请求域,我们可以直接将这个request传入service,这样看起来也确实可以,但是假如业务方法很多,每个方法都需要token,难道还需要每个方

qq_43750656的博客 1991

Flink 运维监控与指标采集实战(Prometheus + Grafana 全流程)

Flink运维监控在实时计算场景中至关重要,因为Flink作业需要7×24小时稳定运行,对性能、资源、故障感知和状态变化的实时监控是保障系统稳定的关键。Flink自带的Metrics系统提供了基本的监控能力,但为了更全面的监控,Prometheus+Grafana成为最主流的解决方案。通过配置PrometheusReporter和Grafana仪表盘,可以实现对Flink作业的全面监控,包括CPU、内存、吞吐量、Checkpoint状态、KafkaSink性能等关键指标。此外,自定义指标采集和报警机制的配置

晴天彩虹雨的博客 1368

别再无脑调打码平台了!聊聊 ddddocr 在爬虫验证码识别中的真实边界

ddddocr是国产开源社区的优秀成果,它大幅降低了验证码识别的入门门槛。工具的自由不等于工程的自由。真正的能力不在于“会不会调库”,而在于能否准确判断工具的适用边界、构建鲁棒的容错机制、并在成本与效果之间做出理性权衡。下次当你准备在爬虫中加入验证码识别时,不妨先问自己:这个站点的验证码类型是否在ddddocr的能力圈内?我的预处理是否针对该站点做过专项调试?我的失败重试和监控机制是否完备?如果三个问题都能肯定回答,那么ddddocr就是你的利器;如果有任何一个犹豫,请先补齐短板再上路。评论区交流:你用。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1281

TheadLocal

一:ThreadLocal的核心机制 1:每个Thread线程内部都有一个Map。 2:Map里面存储线程本地对象(key)和线程的变量副本(value) Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰 get(...

eafun_888的博客 439

Android 系统状态栏中实现高斯模糊背景

至此,我们已经完成了在 Android 系统的 SystemUI 中实现高斯模糊背景的过程。通过以上的布局和代码修改,我们可以在状态栏中看到具有高斯模糊效果的背景。同时,为了在不同版本的 Android 系统上适配高斯模糊效果,可能需要进行一些额外的代码调整和兼容性处理。希望本文能够帮助你理解如何在 Android 系统的 SystemUI 中实现高斯模糊背景,并通过提供的源代码进行相应的开发和调试。首先,我们需要在 SystemUI 的布局文件中添加一个用于展示高斯模糊背景的视图。属性来开启模糊效果。

CyberSparkZ的博客 870

ThreadLocal 学习笔记

对 Threadlocal 理解 ThreadLocal 是为了解决线程间同步而创建的一个新的思路。简单来说就是每个线程都保存一个变量副本。 如果在 Thread 内部定义一个 field 变量,也可以解决这个问题。 这样就需要定义一个新的 Thread 类,来解决这个问题。每一次一个新的变量都需要这个 case,but,实际这个新的类,与 thread 本身并没有关系。 所以最好有一种方式,可以...

怪咖先森的博客 292

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: SpringBoot集成rabbitmq:java.net.ConnectException
下一篇: Redis系列(一)-redis简介
GraceJava
博客等级 码龄10年 131粉丝 71原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值