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的深度解读

    一 J2SE的原始描述 This class provides thread local variables These variables differ from their normal counterparts in that eac
  • 深入理解ThreadLocal源码

    1 预备知识 强软弱虚引用 在Java中有四种引用的类型 强引用 软引用 弱引用 虚引用 设计这四种引用的目的是可以用程序员通过代码的方式来决定对象的生命周期 方便GC 强引用 强引用是程序代码中最广泛使用的引用 如下 Object o n
  • ThreadLocal和ThreadLocalMap

    1 ThreadLocal是什么 是用来存放我们需要能够线程隔离的变量的 那就是线程本地变量 也就是说 当我们把变量保存在ThreadLocal当中时 就能够实现这个变量的线程隔离了 entry中的key使用了弱引用 static clas
  • 一篇文章,从源码深入详解ThreadLocal内存泄漏问题

    原创文章 经验总结 从校招到A厂一路阳光一路沧桑 详情请戳www coderccc com 1 造成内存泄漏的原因 threadLocal是为了解决对象不能被多线程共享访问的问题 通过threadLocal set方法将对象实例保存在每个线
  • java 读写中文文本

    读取中文文本 要设置gbk格式 该格式中文可以识别 英文也可以 reader new InputStreamReader new FileInputStream filename gbk 拓展 分词 分词时候 读取中文文本 TokenStr
  • Java读配置文件

    读取配置文件的方案 public String getProperty String name String k Properties prop new Properties String key null String val null
  • ThreadLocal详解

    如果有兴趣了解更多相关内容 欢迎来我的个人网站看看 瞳孔空间 一 基本介绍 ThreadLocal类能提供线程内部的局部变量 这种变量在多线程环境下访问时能保证各个线程的变量相对独立于其他线程内的变量 ThreadLocal实例通常来说都是
  • SimpleDateFormat线程不安全及解决办法

    以前没有注意到SimpleDateFormat线程不安全的问题 写时间工具类 一般写成静态的成员变量 不知 此种写法的危险性 在此讨论一下SimpleDateFormat线程不安全问题 以及解决方法 为什么SimpleDateFormat不
  • pthread_key_t 和 pthread_key_create 方法如何工作?

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

    我正在编写一个 servlet 它通过访问和修改数据库中的某些表来处理每个请求 我希望与数据库的连接是线程安全的 我不想为此使用现有的库 框架 spring hibernate 等 我知道我可以通过以下方式使用 java 的 ThreadL
  • 将 Spring 应用程序上下文传递到与请求关联的 ThreadLocal 是否安全?

    在 JPA 中我想利用PrePersist带注释的方法来执行一些操作 而不是制作实现单例模式 使用 getInstance 等 所需的东西 我想知道是否通过 ThreadLocal 我在请求后关闭释放 传递 Spring Applicati
  • ThreadLocal - 用作带有 spring-boot 的 REST API 的上下文信息

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

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

    我编写了一个授权系统 它依赖于代表当前用户的对象 为了简化编程并提高性能 我想在用户登录后将这些对象保存在 ThreadLocal 中 它看起来像这样 public class UserCache private static final
  • .NET 中本地可继承线程

    NET 4 0 推出ThreadLocal
  • SecurityContextHolder 提供错误的用户详细信息

    在我的应用程序中 我们正在捕获每笔交易的用户详细信息SecurityContextHolder认证对象 但它给出了错误UserID它似乎 以下是代码片段供您参考 安全上下文 xml 弹簧安全 3 2
  • 在实例变量中使用 ThreadLocal

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

    我什么时候应该使用ThreadLocal https docs oracle com javase 8 docs api java lang ThreadLocal html多变的 它是如何使用的 一种可能 也是常见 的用途是当您有一些非线
  • Spring Security:多个ThreadLocals中有相同的SecurityContext实例,它是如何工作的?

    我有一些关于 Spring Security 3 0 5 和 SecurityContext 的问题 首先 我尝试总结一下我所知道的 SecurityContextHolder 存储 SecurityContext 在Request之间 S
  • 变量的同步和本地副本

    我正在查看一些具有以下习惯用法的遗留代码 Map

随机推荐

  • 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