线程安全之 - ThreadLocal

article/2025/9/14 8:18:08

ThreadLocal的底层原理

  1. ThreadLocal是Java中所提供的线程本地存储机制(线程内共享),可以利⽤该机制将数据缓存在某个线程内部,
    该线程可以在任意时刻、任意⽅法中获取缓存的数据;
  2. ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对
    象)中都存在⼀个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的
    值;
  3. 如果在线程池中使⽤ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使⽤完之后,应该要
    把设置的key,value,也就是Entry对象进⾏回收,但线程池中的线程不会回收,⽽线程对象是通过
    强引⽤指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收,
    Entry对象也就不会被回收,从⽽出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,⼿ 动调⽤ThreadLocal的remove⽅法,⼿动清除Entry对象
  4. ThreadLocal经典的应⽤场景就是连接管理(⼀个线程持有⼀个连接,该连接对象可以在不同的⽅
    法之间进⾏传递,线程之间不共享同⼀个连接)

ThreadLocal的原理是什么,使用场景有哪些?

Thread类中有两个变量threadLocals和inheritableThreadLocals,二者都是ThreadLocal内部类ThreadLocalMap类型的变量,我们通过查看内部内ThreadLocalMap可以发现实际上它类似于一个HashMap。在默认情况下,每个线程中的这两个变量都为null:

ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

只有当线程第一次调用ThreadLocal的set或者get方法的时候才会创建他们。

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;
}

除此之外,每个线程的本地变量不是存放在ThreadLocal实例中,而是放在调用线程的ThreadLocals变量里面。也就是说,ThreadLocal类型的本地变量是存放在具体的线程空间上,其本身相当于一个装载本地变量的载体,通过set方法将value添加到调用线程的threadLocals中,当调用线程调用get方法时候能够从它的threadLocals中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的threadLocals中,所以不使用本地变量的时候需要调用remove方法将threadLocals中删除不用的本地变量,防止出现内存泄漏。

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

ThreadLocal有哪些内存泄露问题,如何避免?

每个Thread都有一个ThreadLocal.ThreadLocalMap的map,该map的key为ThreadLocal实例,它为一个弱引用,我们知道弱引用有利于GC回收。当ThreadLocal的key == null时,GC就会回收这部分空间,但是value却不一定能够被回收,因为他还与Current Thread存在一个强引用关系,如下:
在这里插入图片描述

由于存在这个强引用关系,会导致value无法回收。如果这个线程对象不会销毁那么这个强引用关系则会一直存在,就会出现内存泄漏情况。所以说只要这个线程对象能够及时被GC回收,就不会出现内存泄漏。如果碰到线程池,那就更坑了。 那么要怎么避免这个问题呢? 在前面提过,在ThreadLocalMap中的setEntry()、getEntry(),如果遇到key == null的情况,会对value设置为null。当然我们也可以显示调用ThreadLocal的remove()方法进行处理。 下面再对ThreadLocal进行简单的总结:
● ThreadLocal 不是用于解决共享变量的问题的,也不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制。这点至关重要。
● 每个Thread内部都有一个ThreadLocal.ThreadLocalMap类型的成员变量,该成员变量用来存储实际的ThreadLocal变量副本。
● ThreadLocal并不是为线程保存对象的副本,它仅仅只起到一个索引的作用。它的主要木得视为每一个线程隔离一个类的实例,这个实例的作用范围仅限于线程内部。


http://chatgpt.dhexx.cn/article/JEsw5yIp.shtml

相关文章

线程锁(ReentrantLock、synchronized)为何不能用作分布式锁

为什么使用分布式锁 分布式锁实现目前有三种: 数据库乐观锁;ZooKeeper的分布式锁;Redis的分布式锁; 在以前单体架构Web应用场景下,我们可以使用ReentrantLock或synchronized进行上锁,保证资源安全,现如今大…

Redis分布式锁真的安全吗?

大家好,今天我们来聊一聊Redis分布式锁。 首先大家可以先思考一个简单的问题,为什么要使用分布式锁?普通的jvm锁为什么不可以? 这个时候,大家肯定会吧啦吧啦想到一堆,例如java应用属于进程级,…

ThreadLocal能解决线程安全问题?胡扯!本文教你正确的使用姿势【享学Java】

跟对领导很重要:愿意教你的,并且放手让你做的领导要珍惜。 目录 前言正文ThreadLocal是什么?ThreadLocal怎么用?局限性InheritableThreadLocal向子线程传递数据开源框架使用示例 ThreadLocal不能解决共享变量的线程安全问题Thread…

Java线程安全详细总结

以下是我的PPT文档,不知道怎么复制到博客,只能一个一个插入图片发上来了。感觉总结的不错,分享一下。 文档地址:http://download.csdn.net/detail/csujiangyu/9526641

分布式系统详解--基础知识(线程)

分布式系统详解--基础知识(线程) 一、导读 前面跟大家讲了一下 分布式系统详解--基础知识(概论) ,可以稍微了解一下大体上分布式是怎么一回事了。这片篇文章主要是讲述一下线程的问题分别介绍一下,什么线…

分布式项目线程安全问题(电商扣减库存的安全问题1)

电商减库存存在的安全问题 Override public void deductStock(Map<Long, Integer> skuMap) {for (Map.Entry<Long, Integer> entry : skuMap.entrySet()) {Long skuId entry.getKey();Integer num entry.getValue();// 查询skuSku sku getById(skuId);// 判断…

分布式项目中 如何保证线程安全问题?-------ZooKeeper

前沿&#xff1a; 上篇文章我们聊到了在解决分布式项目中线程安全问题&#xff0c;提到解决方案还有其他的&#xff0c;那么在此提出 基于 zookeeper 解决分布式项目中的线程安全问题 也是目前市面上比较流行的。做为一个高级开发工程师也是必须要学习的。 ZooKeeper是什么东…

分布式线程安全(redis、zookeeper、数据库)

https://blog.csdn.net/u010963948/article/details/79006572 Q:一个业务服务器&#xff0c;一个数据库&#xff0c;操作&#xff1a;查询用户当前余额&#xff0c;扣除当前余额的3%作为手续费 synchronized lock db lock Q&#xff1a;两个业务服务器&#xff0c;一个数据库&…

分布式集群中如何保证线程安全?

目录 分布式集群中的线程安全问题 解决方法 串行化 分布式锁 Redis如何实现呢&#xff1f; 问题&#xff1a;setnx刚好获取到锁&#xff0c;业务逻辑出现异常&#xff0c;导致锁无法释放 问题&#xff1a;可能会释放其他服务器的锁。 问题&#xff1a;删除操作缺乏原子…

java outlook 发送邮件_基于java使用JavaMail发送邮件

一、邮件的相关概念 邮件协议。主要包括&#xff1a; SMTP协议&#xff1a;Simple Mail Transfer Protocol&#xff0c;即简单邮件传输协议&#xff0c;用于发送电子邮件 POP3协议&#xff1a;Post Office Protocol 3&#xff0c;即邮局协议的第三个版本&#xff0c;用于接收邮…

java 发邮件(有正文,有图片,有附件)

一 需求: 1 java实现邮件发送 2 发送内容: ① 正文: 图片说明和图片 ② 附件一: 图片作为附件发送 ③ 附件二: Excel表格 二 思路: 1首先创建一个 Java 工程&#xff0c;把下载好的 javax.mail.jar 作为类库加入工程 2邮件创建步骤: 配置连接邮件服务器的参数( 邮件服务器SM…

java接收邮件_Java实现邮件收发

一. 准备工作 1. 传输协议 SMTP协议-->发送邮件: 我们通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器) POP3协议-->接收邮件: 我们通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器) 2. 邮件收发原理 闪电…

java发送邮件工具类

1. 普通java实现邮件发送 1.1 创建maven项目&#xff0c;配置pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&qu…

java发送邮件带附件

一、 开启SMTP服务 1.基本都在邮箱设置里&#xff0c;开启后会获得神秘代码&#xff0c;后面有用。 2.记得添加依赖&#xff0c;或者自己添加jar包。 <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>…

java 邮件模板

邮件发送代码可参照 java 发送邮件 1.情形 邮件发送代码可参照上述&#xff0c;本例只说明如果读取模板文件。公司定义模板较为复杂的情况&#xff0c;可采用此类发送方式 2. 模板 2.1 resource 建立模板 2.2 ftl 模板如下 <p>您好&#xff0c;${name}&#xff0c;您…

使用JAVA实现邮件发送功能

一、准备工作 小编今天以 QQ邮箱 进行演示操作。 想要使用代码操作邮箱发送邮件&#xff0c;需要在邮箱设置中申请开通 POP3/SMTP 服务。 接下来跟着小编的图文一步一步的操作开通吧&#xff01; 1.1 登录网页QQ邮箱&#xff0c;点击页面顶部设置按钮。 1.2 点击后会打开邮箱…

java发送qq邮件

1.登录qq邮箱 1&#xff09;点击设置 2&#xff09;点击账户 3&#xff09;开启第一个服务&#xff0c;我已经开过了 4&#xff09;开启验证&#xff08;让你发送指定内容到某个号码&#xff09;&#xff0c;完成后点击我已发送&#xff0c;就会出现授权码&#xff0c;授权码很…

java实现邮件发送

一.第一步:导入两个jar包。 activation.jar 和 mail.jar, 一定要添加到构建路径(不然找不到包) 两个用于Java发送邮件的jar包-Java文档类资源-CSDN下载 二、创建邮箱工具类:Mail.java import java.util.*; import java.io.*; import javax.mail.*; import javax.m…

Java(81):Java发邮件简单示例

Java Email jar包下载地址&#xff1a;JavaMail API https://www.oracle.com/java/technologies/javamail.html JavaMail 右侧下载&#xff0c;选择jar包下载 API文档参考&#xff1a;JavaMail API documentation https://javaee.github.io/javamail/docs/api/ 或直接引用…

java发送qq邮件_「java发邮件」Java 通过SMTP实现发送QQ邮件 - seo实验室

java发邮件 在Eclipse中创建项目&#xff0c;并把javax.amil.jar和commons-email-1.5,jar复制到项目中 链接&#xff1a;https://pan.baidu.com/s/1sQjA1GEpKi6IJJRGHKxjeA 密码&#xff1a;4ene 添加步骤&#xff1a; 1.首先在项目下创建一个文件夹&#xff0c;保存我们的jar包…