当前位置: 首页 > news >正文

ThreadLocal的那些杂事

我们都知道多个线程需要对一个共享变量进行修改操作的时候容易出现数据安全问题,如下如所示线程操作共享变量的图:

在这里插入图片描述
为了保证线程安全,一般使用者在访问共享变量的时候需要进行同步措施才能保证线程安全性,常见的方法是使用synchronized或者Lock锁机制。
有没有一种类似JMM中线程工作内存机制,也就是每个线程都有一份变量副本,然后每个线程对共享变量进行访问的时候访问的是自己线程的变量副本呢? 其实在Java中就有这样的东西,它就是ThreadLocal。
在这里插入图片描述
当创建一个ThreadLocal变量,每个线程都会复制一个变量到自己的本地内存,然后每个线程对共享资源进行访问的时候访问的都是线程自己的变量,这样就不会存在线程安全问题。
1、ThreadLocal的原理
ThreadLocal是Java中所提供的线程本地存储机制,可以利用该机制将数据缓存到某个线程内部,这样该线程可以在任意时刻、任意位置都可以获取缓存的数据,下图是ThreadLocal的实现原理图:
在这里插入图片描述
ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,这个Map的可以是ThreadLocal对象,value存储的是需要缓存的值。
在做set操作的时候,key是当前的线程,value是需要存储的值,如下是ThreadLocal的源代码:
在这里插入图片描述
在get数据的时候,其实是拿到当前的线程然后去ThreadLocalMap中去找这个key(也是当前的线程)对应的value值,get的源码如下所示:
在这里插入图片描述
关于ThreadLocal知识点的总结:
(a)ThreadLocalMap是ThreadLocal的内部类,它是真正存储数据的类,其内部还有一个Entry内部类,ThreadLocalMap就是使用一个Entry 数组来存储数据。
在这里插入图片描述
(b)ThreadLocalMap的默认初始容量为16;当ThreadLocalMap中的元素数量达到容量的2/3时会触发扩容机制,每次扩容ThreadLocalMap 的容量会是原来的2倍,这样的目的是减少哈希冲突。
在这里插入图片描述
2、ThreadLocal的内存泄漏问题
在这里插入图片描述
ThreadLocal存在内存泄漏的原因是因为ThreadLocalMap中包含一个Entry数组,Entry对象中的Key属于弱引用(Entry extends WeakReference<ThreadLocal<?>>),那么Entry对象中的Key可以被GC自动回收。 如果当Entry对象中的Key被GC自动回收后,对应的在堆上的ThreadLocal对象也会被GC回收掉了,经过一轮GC之后的结果如下图所示:
在这里插入图片描述
此时ThreadLocal中对应的value值依然被Entry引用,不能被GC自动回收,所以value是不能被GC自动回收的,这种情况下就会存在内存泄露的问题。 在正常情况下,ThreadLocal对象使用完之后会要把Entry对象进行回收,假设在线程池中使用ThreadLocal的时候,由于线程池中的核心线程不会被回收,这就导致了Entry上的value不会被回收,从而出现内存泄漏问题。 解决ThreadLocal内存泄漏的方案是在使用完ThreadLocal对象之后,手动调用ThreadLocal的remove()来清除Entry对象,remove方法的源码如下所示:
在这里插入图片描述
3、ThreadLocal应用场景
(1)存储用户的数据 在Web应用中,ThreadLocal可以存储用户数据(如用户的身份信息、权限)。每个线程可以通过ThreadLocal来隔离不同用户的数据。
(2)事务管理
在数据库操作过程中,ThreadLocal可以用于管理事务。如在每个线程中维护数据库连接,使得在同一线程中的操作共享相同的事务上下文。
在这里插入图片描述
(3)隔离线程,存储一些线程不安全的工具对象 在处理线程不安全的对象(如SimpleDateFormat)时,利用ThreadLocal 来存储DateForma实例,这样确保每个线程都拥有自己独立的 SimpleDateFormat实例,从而避免了线程间的竞争导致数据安全问题。
(4)跨层传递共享信息 假设A、B、C、D 这几个类需要互相传递共享信息,如果在每个方法都声明一个参数来接收共享信息就降低代码的可维护性,此时可以用ThreadLocal来存储共享变量的信息,我们在A类中存值,B、C、D便可以可以获取共享变量值,如下图所示:
在这里插入图片描述
4、InheritableThreadLocal ThreadLocal类是不能提供子线程访问父线程的本地变量的,而InheritableThreadLocal类则可以做到这个功能,如下代码:
在这里插入图片描述
通过InheritableThreadLocal在主线程中子线程t1就可以获取到主线程中设置的用户信息,这样实现了子线程与主线程之间的通信

http://www.xdnf.cn/news/68455.html

相关文章:

  • 2025.4.21日学习笔记 JavaScript String、Array、date、math方法的使用
  • spark—SQL3
  • [免费]SpringBoot+Vue博物馆(预约)管理系统【论文+源码+SQL脚本】
  • 华为数字化转型“三阶十二步法“:战略驱动、系统布局与敏捷落地的实践框架
  • 29个常见的Terraform 面试问题
  • <数据集>小船识别数据集<目标检测>
  • Python基础总结(九)之推导式
  • AI 赋能企业架构之路:五大关键路标点深度剖析
  • C++ 封装成DLL,C#调用
  • 数字虹膜:无网时代的视觉密语 | 讨论
  • 第1 篇:你好,时间序列!—— 开启时间数据探索之旅
  • liunx中常用操作
  • 深入探索Qt异步编程--从信号槽到Future
  • 483. 找到字符串中所有的字母异位词
  • Linux 进程与线程间通信方式及应用分析
  • 分布式数据库TiDB:架构、核心特性与生产实践(分库分表)
  • 基于selenium框架的web应用自动化测试系统的设计与实现 毕业论文开题
  • Linux-网络基础
  • Spring_MVC 高级特性详解与实战应用
  • HTTP参数污染
  • Cribl 利用表向event 中插入相应的字段-example-01
  • 为零基础及不同背景学习者设计的 人工智能全栈学习路线图
  • git 版本提交规范
  • Linux 网络基础(三) TCP/IP协议
  • AI大模型 —— 国产大模型 —— 华为大模型
  • 卸载工具:IObit Uninstaller Pro v14.3.0 中文绿色专业便携版
  • IO流--字节流详解
  • linux安装mysql数据库
  • 如何下载适用于超宽屏显示的Google Chrome浏览器
  • CMake笔记:find_package工作原理