【图解】ThreadLocal底层实现原理

2023-11-16

1、ThreadLocal的底层原理图
请添加图片描述
说明:Thread中有threadLocals成员变量,threadLocal会在threadlocal首次set时进行赋值【这会在非main线程中复现,主线程启动即会进行赋值】,ThreadLocalMap是ThreadLocal的静态内部类,在set时,会将我们新建threadLocal引用地址作为key,以此封装成一个Entry<ThreadLocal<?>,Object>对象,可以存在多个不同的threadlocal,如果set的引用地址相同,就会进行覆盖,此处key的类型ThreadLocal继承弱引用也是会造成内存泄露的主要原因,在下面源码中会对此段相关点分别说明。

2.源码解析

package com.adun.test_threadlocal;

/**
 * @author ADun
 * @date 2022/4/27 11:14
 */
public class ThreadLocalTest {

    public static final ThreadLocal<Integer> threadLocal = new ThreadLocal();
    public static final ThreadLocal<Integer> threadLocal2 = new ThreadLocal();

    public static void main(String[] args) {
        threadLocal.set(1234);
        Integer num1 = threadLocal.get();
        System.out.println(num1);
        Integer num2 = threadLocal.get();
        System.out.println(num2);

        threadLocal2.set(111);
        System.out.println(threadLocal2.get());
		//最后必须remove,避免内存泄露
		threadLocal.remove();
        threadLocal2.remove();
    }
}

ThreadLocal中的set方法以及涉及到的相关方法

    public void set(T value) {
        Thread t = Thread.currentThread();
        //获取当前线程的threadLocals
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        	//如果获取不到当前线程的threadlocals成员变量,则新建并将value放入
            createMap(t, value);
    }
 	/**
 	 * 获取当前线程的threadlocals
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

	/**
	 * 创建ThreadLocalMap,并对当前线程的threadlocals进行赋值
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocal静态内部类ThreadLocalMap

	/**
	  * 初始化ThreadLocalMap的构造器
      * Construct a new map initially containing (firstKey, firstValue).
      * ThreadLocalMaps are constructed lazily, so we only create
      * one when we have at least one entry to put in it.
      */
     ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
         table = new Entry[INITIAL_CAPACITY];
         int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
         table[i] = new Entry(firstKey, firstValue);
         size = 1;
         setThreshold(INITIAL_CAPACITY);
     }
     
	  /**
	   * 玩当前线程的threadlocals对应的threadLocalMap中赋值
	   * 用户new的threadlocal的引用地址作为key,value作为value进行赋值构建Entry对象,如果已存在,则进行覆盖
       * 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<ThreadLocal<?>,Object>数组,存放多个threadlocal的数据
          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();
      }

ThreadLocalMap内部类Entry

		/**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
			
			//Entry继承弱引用,弱引用的特点是在jvm进行gc扫描中直接进行回收
			//这种操作既有可能k的引用被回收,而v的值失去所有到达的引用,造成内存泄露,所以在最后必须remove
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【图解】ThreadLocal底层实现原理 的相关文章

  • Java 中等效的并行扩展

    我在 Net 开发中使用并行扩展有一些经验 但我正在考虑在 Java 中做一些工作 这些工作将受益于易于使用的并行库 JVM 是否提供任何与并行扩展类似的工具 您应该熟悉java util concurrent http java sun
  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • Android MediaExtractor seek() 对 MP3 音频文件的准确性

    我在使用 Android 时无法在eek 上获得合理的准确度MediaExtractor 对于某些文件 例如this one http www archive org download emma solo librivox emma 01
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • 操作错误不会显示在 JSP 上

    我尝试在 Action 类中添加操作错误并将其打印在 JSP 页面上 当发生异常时 它将进入 catch 块并在控制台中打印 插入异常时出错 请联系管理员 在 catch 块中 我添加了它addActionError 我尝试在jsp页面中打
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • getResourceAsStream() 可以找到 jar 文件之外的文件吗?

    我正在开发一个应用程序 该应用程序使用一个加载配置文件的库 InputStream in getClass getResourceAsStream resource 然后我的应用程序打包在一个 jar文件 如果resource是在里面 ja
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • 如何从指定日期获取上周五的日期? [复制]

    这个问题在这里已经有答案了 如何找出上一个 上一个 星期五 或指定日期的任何其他日期的日期 public getDateOnDay Date date String dayName 我不会给出答案 先自己尝试一下 但是 也许这些提示可以帮助
  • 如何在桌面浏览器上使用 webdriver 移动网络

    我正在使用 selenium webdriver 进行 AUT 被测应用程序 的功能测试自动化 AUT 是响应式网络 我几乎完成了桌面浏览器的不同测试用例 现在 相同的测试用例也适用于移动浏览器 因为可以从移动浏览器访问 AUT 由于它是响
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 使用 xpath 和 vtd-xml 以字符串形式获取元素的子节点和文本

    这是我的 XML 的一部分

随机推荐

  • LeetCode 刷题 454

    看完的想法就是暴力解 不过 不用想都知道超时了 想了一会还是没有思路 就去学习一下别人怎么做的 感觉就是二数相加 再灵活一点 这没想到 还是练得少了 四个数组 两两分组 第一组 遍历 相加 将各个和的个数存入map中 第二组 查找 0于各个
  • Unfortunately you can‘t have non-Gradle Java modules and Android-Gradle modules in one project

    解决步骤 Close Project Close Android Studio IDE 删除 idea 目录和 iml 文件 Open Android Studio IDE And Project 步骤转自 https blog csdn
  • 高并发下接口幂等性的解决方案

    欢迎关注方志朋的博客 回复 666 获面试宝典 一 背景 我们实际系统中有很多操作 是不管做多少次 都应该产生一样的效果或返回一样的结果 例如 前端重复提交选中的数据 应该后台只产生对应这个数据的一个反应结果 我们发起一笔付款请求 应该只扣
  • js连接web3,连接小狐狸metamask钱包,实现链不对后切换网络和创建网络

    直接上代码 我这里吧所有配置都改成正式的链56 一旦用户的小狐狸钱包现在的链不一致 就询问切换网络 没有就创建网络 网络切换成功后 收到监听 重新连接一下web3 就是重新调用一些connectWeb3这个方法 再连接合约 connectW
  • 20230918使用ffmpeg将mka的音频转为AAC编码以便PR2023来识别

    20230918使用ffmpeg将mka的音频转为AAC编码以便PR2023来识别 2023 9 18 20 58 ffmpeg i 1 mka acodec aac 1 mp4 ffmpeg i 1 mka vn c a aac 2 aa
  • MYSQL  Replication  主从配置

    MYSQL Replication 主从配置 MySQL Replication 又叫做AB复制或者主从复制 它主要用于MySQL的实时备份或者读写分离 在配置之前先做一下准备工作 配置两台mysql服务器 或者在一台服务器上配置两个端口也
  • IAR for AVR中的位定义使用

    目的 打开IAR for AVR中的位定义 在单片机的编程中 经常会遇到 UCSRB 1 lt lt TXEN 这样的表达式 查看定义可以在头文件中看到 define TXEN 3 表示的是 1 lt lt TXEN 是将1左移3位 然后赋
  • 使用 pandas 导出数据

    import pandas as pd In 58 df pd DataFrame houselist In 59 df In 61 df to csv lianjia 转载于 https www cnblogs com polly lin
  • 字符串 反转

    LC 反转字符串 swift 实现 var s Character h e l l o reversString s print s func reversString s inout Character var temp Characte
  • SQLServer 数据 迁移转到Mysql 中

    目录 一 SQLServer 转Mysql 的几种方法 二 DB2DB 使用 2 1 迁移出错后的报错日志 三 补齐出现问题的表 问题 原因 解决 一 SQLServer 转Mysql 的几种方法 sqlServer 转mysql 最大的问
  • vue3(二)配置标题和服务代理

    这里的标题如何改成自己的标题 第一步 修改vue config js transpileDependencies true chainWebpack config gt config plugin html tap args gt args
  • android广播数据大小限制,android 限制广播消息的接收者

    原文 http blog csdn net mingli198611 article details 17762149 在android系统中sendBroadcast和BroadcastReceiver 只要BroadcastReceiv
  • Linux学习(未完待续。。。)

    安装好ubuntu 并且注册用户成功后 给root设置密码 sudo passwd root 先输入自己的密码 然后输入root的密码 再确认 密码都是不可见的 不是键盘坏了 显示日期格式 date Y m d H M S m month
  • Hyperledger Fabric学习笔记——7.链码安装、实例化、执行

    1 智能合约 执行环境 以太坊虚拟智能合约执行环境EVM fabric执行环境是docker 链码 是应用层和区块链底层的中间点 每一个链码执行环境是一个独立的docker 使用GRPC协议与背书节点通信 只有背书节点才能运行智能合约 链码
  • 如何配置服务器自动监控并报警

    作者 一个懂技术的运营 链接 https www zhihu com question 21073555 answer 106131463 来源 知乎 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 如果是初创型公司 机
  • Vue 的forEach和push

    tagTacticsFilter tacticsTag let data let tacticsTagList this tacticsTagList let attrList tacticsTag split attrList forEa
  • 真正的学懂三极管入门篇(经典)

    不要让温床称为埋葬你的坟墓 要有危机意识 忧患意识 要为明天考虑 禁忌 安于现状 不求上进 不懂得学习 不能恰当的处理工作和学习关系 没有把英语放在第一位
  • uniApp中 nvue和vue开发 小结

    最近接手uniapp开发 对遇到的问题进行总结 什么是nvue nvue native vue 原生渲染 为何要用nvue开发 weex 有个很大的问题是它只是一个高性能的渲染器 没有足够的API能力 比如各种push sdk集成 蓝牙等能
  • 电梯调度算法-C++

    1 算法解析 扫描算法 SCAN 又称电梯调度算法 SCAN算法是磁头前进方向上的最短查找时间优先算法 它排除了磁头在盘面局部位置上的往复移动 SCAN算法在很大程度上消除了SSTF算法的不公平性 但仍有利于对中间磁道的请求 电梯调度算法是
  • 【图解】ThreadLocal底层实现原理

    1 ThreadLocal的底层原理图 说明 Thread中有threadLocals成员变量 threadLocal会在threadlocal首次set时进行赋值 这会在非main线程中复现 主线程启动即会进行赋值 ThreadLocal