ThreadLocal和ThreadLocalMap

2023-11-06

 

1.ThreadLocal是什么?

是用来存放我们需要能够线程隔离的变量的,那就是线程本地变量。也就是说,当我们把变量保存在ThreadLocal当中时,就能够实现这个变量的线程隔离了。

 

 

entry中的key使用了弱引用:

static class ThreadLocalMap {

        /**
         * 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(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

强、软、弱、虚引用传送门:https://zhuanlan.zhihu.com/p/133527214

 

2.为什么使用entry中的key使用弱引用?

弱引用具体指的是java.lang.ref.WeakReference类。

对对象进行弱引用不会影响垃圾回收器回收该对象,即如果一个对象只有弱引用存在了,则下次GC将会回收掉该对象(不管当前内存空间足够与否)。

再来说说内存泄漏,假如一个短生命周期的对象被一个长生命周期对象长期持有引用,将会导致该短生命周期对象使用完之后得不到释放,从而导致内存泄漏。

因此,弱引用的作用就体现出来了,可以使用弱引用来引用短生命周期对象,这样不会对垃圾回收器回收它造成影响,从而防止内存泄漏。

1.为什么ThreadLocalMap使用弱引用存储ThreadLocal?

假如使用强引用,当ThreadLocal不再使用需要回收时,发现某个线程中ThreadLocalMap存在该ThreadLocal的强引用,无法回收,造成内存泄漏。

因此,使用弱引用可以防止长期存在的线程(通常使用了线程池)导致ThreadLocal无法回收造成内存泄漏。

2.那通常说的ThreadLocal内存泄漏是如何引起的呢?

我们注意到Entry对象中,虽然Key(ThreadLocal)是通过弱引用引入的,但是value即变量值本身是通过强引用引入。

这就导致,假如不作任何处理,由于ThreadLocalMap和线程的生命周期是一致的,当线程资源长期不释放,即使ThreadLocal本身由于弱引用机制已经回收掉了,但value还是驻留在线程的ThreadLocalMap的Entry中。即存在key为null,但value却有值的无效Entry。导致内存泄漏。

但实际上,ThreadLocal内部已经为我们做了一定的防止内存泄漏的工作

/**
         * Expunge a stale entry by rehashing any possibly colliding entries
         * lying between staleSlot and the next null slot.  This also expunges
         * any other stale entries encountered before the trailing null.  See
         * Knuth, Section 6.4
         *
         * @param staleSlot index of slot known to have null key
         * @return the index of the next null slot after staleSlot
         * (all between staleSlot and this slot will have been checked
         * for expunging).
         */
        private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // expunge entry at staleSlot
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

该方法的作用:

擦除某个下标的Entry(置为null,可以回收),同时检测整个Entry[]表中对key为null的Entry一并擦除,重新调整索引。

该方法,在每次调用ThreadLocal的get、set、remove方法时都会执行,即ThreadLocal内部已经帮我们做了对key为null的Entry的清理工作。

但是该工作是有触发条件的,需要调用相应方法,假如我们使用完之后不做任何处理是不会触发的。

 

 

 

 

知乎上看到一张图画的很清晰:ThreadLocal其实只是个符号意义,本身不存储变量,仅仅是用来索引各个线程中的变量副本

 

 

3.Spring如何保证bean在多线程环境下的安全?

1.为什么会有线程安全问题?

例如Spring的jdbc的connection,如果在一次请求中多次操作同一个数据库表,然后从线程池(连接池)中获取connection,每次都有可能拿到不同的connection,这样就没有办法保证事务-----》因为不同的connection,直接变成了分布式事务

从而产生一系列问题

 

2.如何解决?

首先,肯定Spring的单例缓存池使用的是concurrentHashmap,保证写入容器的线程安全

然后,使用threadLocal,用来解决从池中获取connection随机问题以及多线程资源竞争问题

 

 

4.threadLocal总结?

  • (强制)在代码逻辑中使用完ThreadLocal,都要调用remove方法,及时清理。

目前我们使用多线程都是通过线程池管理的,对于核心线程数之内的线程都是长期驻留池内的。显式调用remove,一方面是防止内存泄漏,最为重要的是,不及时清除有可能导致严重的业务逻辑问题,产生线上故障(使用了上次未清除的值)。

最佳实践:在ThreadLocal使用前后都调用remove清理,同时对异常情况也要在finally中清理。

  • (非规范)对ThreadLocal是否使用全局static修饰的讨论。

在某些代码规范中遇到过这样一条要求:“尽量不要使用全局的ThreadLocal”。关于这点有两种解读。最初我的解读是,因为静态变量的生命周期和类的生命周期是一致的,而类的卸载时机可以说比较苛刻,这会导致静态ThreadLocal无法被垃圾回收,容易出现内存泄漏。另一个解读,我咨询了编写该规范的对方解释是,如果流程中改变了变量值,下次复用该流程可能导致获取到非预期的值。

但实际上,这两个解读都是不必要的,首先,静态ThreadLocal资源回收的问题,即使ThreadLocal本身无法回收,但线程中的Entry是可以通过remove清理掉的也就不会出现泄漏。第二种解读,多次复用值改变的问题,其实在调用remove后也不会出现。

而如果ThreadLocal不加static,则每次其所在类实例化时,都会有重复ThreadLocal创建。这样即使线程在访问时不出现错误也有资源浪费。

因此,ThreadLocal一般加static修饰,同时要遵循第一条及时清理。

 

该总结来源:https://zhuanlan.zhihu.com/p/91579723

 

 

 

 

 

 

 

 

 

 

 

 

 

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

ThreadLocal和ThreadLocalMap 的相关文章

  • ThreadLocal 适合用在哪些实际生产的场景中?

    在通常的业务开发中 xff0c ThreadLocal有两种典型的使用场景 场景1 xff0c ThreadLocal 用作保存每个线程独享的对象 xff0c 为每个线程都创建一个副本 xff0c 这样每个线程都可以修改自己所拥有的副本 而
  • ThreadLocal的深度解读

    一 J2SE的原始描述 This class provides thread local variables These variables differ from their normal counterparts in that eac
  • assert注意事项

    结论 直接使用 assert field null 可以快速的check属性 但是该方法会在使用反射的时候多一个属性 assertionsDisabled 分析 public class Assert static final boolea
  • ThreadLocal和ThreadLocalMap

    1 ThreadLocal是什么 是用来存放我们需要能够线程隔离的变量的 那就是线程本地变量 也就是说 当我们把变量保存在ThreadLocal当中时 就能够实现这个变量的线程隔离了 entry中的key使用了弱引用 static clas
  • java 读写中文文本

    读取中文文本 要设置gbk格式 该格式中文可以识别 英文也可以 reader new InputStreamReader new FileInputStream filename gbk 拓展 分词 分词时候 读取中文文本 TokenStr
  • C++11中thread_local的使用

    C 11中的thread local是C 存储期的一种 属于线程存储期 存储期定义C 程序中变量 函数的范围 可见性 和生命周期 C 程序中可用的存储期包括auto register static extern mutable和thread
  • pthread_key_t 和 pthread_key_create 方法如何工作?

    我在弄清楚 pthread key t 和 pthread key create 如何工作时遇到一些麻烦 据我了解 每个线程都有 TLS 线程本地存储 并且使用密钥来访问线程本地存储 我不明白的是 当创建密钥时 每个线程都可以使用它吗 假设
  • ThreadLocal 和内存泄漏

    在多个帖子中都提到 不当使用ThreadLocal导致内存泄漏 我正在努力理解内存泄漏是如何发生的ThreadLocal 我想到的唯一场景如下 Web 服务器维护一个线程池 例如 用于 servlet 如果变量在这些线程中 则可能会造成内存
  • Java线程安全数据库连接

    我正在编写一个 servlet 它通过访问和修改数据库中的某些表来处理每个请求 我希望与数据库的连接是线程安全的 我不想为此使用现有的库 框架 spring hibernate 等 我知道我可以通过以下方式使用 java 的 ThreadL
  • 有没有办法使用GCC的__thread完全模拟thread_local?

    C 11 标准包含一个新的补充 thread local 说明符 它使静态变量成为线程局部的 标准 thread local 支持非平凡类型 具有构造函数和析构函数的类型 不幸的是 GCC 仅支持简单类型 thread说明符作为扩展提供 有
  • ThreadLocal - 用作带有 spring-boot 的 REST API 的上下文信息

    我有一些spring boot应用程序 它公开了 REST API 提到的 REST API 是由spring security 一切都很好 但是现在我需要设置上下文 用于服务请求 设置上下文是指根据用户上下文选择数据源 关键是Routin
  • C++:处理线程本地对象销毁

    我有一个日志系统 它基本上使用线程本地缓冲区来记录 这有助于减少锁定 可以将一堆消息写入线程本地缓冲区并一次性刷新 而且由于它是线程本地的 我们可以避免为每个日志消息分配缓冲区 无论如何 问题是在进程退出期间 我们在访问线程本地缓冲区时看到
  • 使用 ThreadLocal 作为数据上下文是个好主意吗?

    使用 ThreadLocal 作为 Web 应用程序中数据的上下文是个好主意吗 这就是它的目的 但请注意删除上下文末尾的 ThreadLocal 否则可能会出现内存泄漏 或者至少会保留未使用的数据太长时间 ThreadLocals 也非常快
  • .NET 中本地可继承线程

    NET 4 0 推出ThreadLocal
  • 在并行任务期间跟踪失效的 WebDriver 实例

    我看到一些使用 Selenium WebDriver 运行并行嵌套循环 Web 压力测试的死实例怪异现象 简单的例子是 比如说 点击 300 个独特的页面 每个页面有 100 次展示 我 成功 获得 4 8 个 WebDriver 实例Th
  • Java 的 ThreadLocal 底层是如何实现的?

    ThreadLocal是如何实现的 它是用 Java 实现的 使用一些从 ThreadID 到对象的并发映射 还是使用一些 JVM 钩子来更有效地完成它 这里的所有答案都是正确的 但有点令人失望 因为它们在某种程度上掩盖了如何聪明Threa
  • 在实例变量中使用 ThreadLocal

    Do Java ThreadLocal如果变量用作实例变量 则它们会生成线程局部值 例如 在生成线程本地对象的方法中 或者它们必须始终是静态的吗 作为一个例子 假设一个典型的场景 其中几个初始化非线程安全类的对象的成本很高 需要在单个静态初
  • ThreadLocal 和 SimpleDateFormat 数组

    使用与中描述的模式非常相似的模式最近的问题 https stackoverflow com questions 10491135 threadlocal for multithreaded access to simpledateforma
  • C++11:重要的线程局部静态变量?

    我有一个X类 class X 我想做这个 void f thread local static X x 实际上我使用的是 gcc 所以关键字是 thread 但我不能 因为你只能有微不足道的 thread locals 对此最好的解决方法是
  • 在 .NET 中使用线程本地存储的最佳实践是什么?

    我的应用程序中有一个要求 我认为可以通过使用线程本地存储来满足 但我想知道这是否是最好避免的事情之一 我读过一些关于这个主题的文章 http www dotnetcoders com web Articles ShowArticle asp

随机推荐

  • DP和HDMI区别

    转自 https www toutiao com i6877677362054595080 在目前市面上显示器接口中 VGA和DVI已经逐渐退出了历史舞台 Type C还算是小众 而DP DisplayPort 与HDMI则成为了主流产品的
  • 普通人在chatGPT的3个赚钱机会

    短短的2个多月内 到处都在讨论ChatGPT 不管你有没有参与其中 以GPT为代表的AI工具已经进化到一个很恐怖的程度了 比如说最近爆火的AutoGPT 能按照一个指令自动干活了 好想试一下 让AutoGPT自动帮我分析福利彩票 ChatG
  • 实现el-form一行中多个el-form-item

    el form item默认一个占一行 利用el row和el col实现一行中多个 注意 el col span 12 中的12是一个占据的列数 默认一列总共24列 通过调整这个数字 可以调整不同列的宽度 如果只使用el col 不在外面
  • c++23中的新功能之一介绍

    一 c 23的目标和延革 c 的标准发展速度在经过c 11的近乎可以称革新的变化之后 开始步入了快车道 有的人在网上说 c 11后的c 语言和c 11以前的c 语言不是一个语言 这有点夸张了 但不可否认 其内容确实变化非常大 很多人可能都没
  • 异步处理机制 多线程

    在处理程序执行流程时 一定要切记 android的处理机制是异步处理 多线程的它并不会因为一个线程处于阻塞状态时其他的线程就不往下执行了 看看代码是不是一个线程的 如果是一个线程的 线面就阻塞了 转载于 https www cnblogs
  • SpringBoot项目将数据源变成Json文件(Jackson2RepositoryPopulatorFactoryBean实现)

    一 项目情景 有时在我们项目当中需要存储一些固定值时 会使用一些配置文件来存储 例如最常见的 json文件 它可以用来存储相应的属性以及属性值 当你需要的时候进行提取 甚至还可以基于这个 json文件写一些条件查询的语句来获得自己需要的值
  • 正则表达式转义字符

    正则表达式的转义字符 除 外 其他字符与自身匹配 点的转义 gt u002E 美元符号的转义 gt u0024 乘方符号的转义 gt u005E 左大括号的转义 gt u007B 左方括号的转义 gt u005B 左圆括号的转义 gt u0
  • 浙大python网_Python爬虫学习(8):浙大软院网络登陆保持

    在浏览器的验证窗口中输入登陆名和密码后 成功后会弹出一个小的新窗口 如果不小心关闭了这个窗口 则就会无法联网 如果说我在一个不带有桌面的Linux系统中 我是不能够通过浏览器接入网络的 虽然提供了不同系统的不同版本的客户端 没有用过 但是还
  • koa文件上传(详解koa-body)

    koa body const koa require koa const koaBody require koa body const path require path const app new koa let app new Koa
  • linux切换目录shell脚本,【Linux命令行与shell脚本编程】教程三——切换目录

    浏览文件系统 1 Linux文件路径 linux文件路径和windows文件路径不同 一个windows文件的路径可能是这样的 C Users John Documents test txt 而Linux的路径是这样的 home John
  • IOS通知中心(观察者模式)[NSNotificationCenter defaultCenter]

    通知机制和KVO都是通过 观察者模式实现的 KVO 即 Key Value Observing 它提供一种机制 当指定的对象的属性被修改后 则对象就会接受到通知 简单的说就是每次指定的被观察的对象的属性被修改后 KVO就会自动通知相应的观察
  • 深入理解计算机系统(第二版) 家庭作业 第十一章

    11 6 A 因为read requesthdrs中已经打印出了请求报头 所以只要打印请求行即可 在doit函数中第一个sscanf语句之后添加下面的语句即可 printf s s s n method uri version B 用火狐浏
  • webM文件解析--基于Matroska和EBML

    1 什么是webM 要说webM 先说Matroska Matroska是一个可扩展的 开源的多媒体容器 说简单点 容器的作用 就是把视频和音频封装到一个文件 使用这种容器的常见文件 一个是MKV 一个就是webM 两者的区别 无非是支持的
  • 大话西游灯谜答案

  • 《拉勾Java高薪课程》阶段一输出 之 持久层框架设计实现及MyBatis源码分析-学习笔记 --菜鸟小回

    阶段一模块一学习笔记 文章目录 阶段一模块一学习笔记 toc 一 自定义持久层框架 1 JDBC问题总结 2 问题解决思路 3 自定义框架设计 4 实际项目目录分析 5 优化 5 1 将测试类方法 5 2 仍存在问题 5 3 解决方式 6
  • SpringBoot访问静态资源html和jsp

    1 新建springBoot项目 1 2 3 4 5 6 2 配置项目JDK 前面已经配置的可以不用配置 1 2 3 把项目配置成maven项目 1 2 3 4 5 6 有maven项目这里结构有test 4 访问json 配置数据库在默认
  • 使用51单片机实现点阵汉字平滑滚动显示

    使用51单片机实现点阵汉字平滑滚动显示 说明 采用的芯片是89C51 LED点阵屏的规格是16 16 同时使用了两个74HC595芯片 字模生成软件在文末有网盘链接 1 连接原理图 整体的电路连接如上图所示 单片机只需要使用三个IO接口 就
  • 云开发小程序要服务器吗,小程序云开发发布还需要服务器吗

    小程序云开发发布还需要服务器吗 内容精选 换一换 AppCube的服务编排 支持对逻辑判断组件 数据处理组件 以及脚本 子服务编排 商业对象等进行可视化组合编排 实现丰富的业务功能 在传统的开发中程序员一般是基于代码进行开发 程序员需要学习
  • (2019.8.20半解决)Solving environment: failed with initial frozen solve. Retrying with flexible solve.Co

    用conda命令在linux安装python库出现上述错误 这里提到了这个问题 有人建议更新conda 我更新后无效 不过 conda不行 但是pip可以安装 问题先这样 后续有时间再仔细研究
  • ThreadLocal和ThreadLocalMap

    1 ThreadLocal是什么 是用来存放我们需要能够线程隔离的变量的 那就是线程本地变量 也就是说 当我们把变量保存在ThreadLocal当中时 就能够实现这个变量的线程隔离了 entry中的key使用了弱引用 static clas