ThreadLocal线程重用时带来的问题

news/2024/9/19 15:36:13/文章来源:https://www.cnblogs.com/xyuanzi/p/18408063

背景

我们都知道ThreadLocal实现了资源在线程内独享,线程之间隔离
实际使用中,ThreadLocal适用于变量在线程间隔离,而在方法或类间共享的场景。比如用户信息,当用户信息需要在多个方法之间传递或者共享使用的时候,同时,每个Tomcat请求的用户信息是私有的。这时可使用ThreadLocal,即直接从线程的ThreadLocal中获取用户信息,而不用在方法的形参中获取。
但是,不当的使用也会带来一些问题,比如在线程池中使用ThreadLocal可能会导致获取到ThreadLocal中的历史数据,造成数据错乱。如Tomcat中使用的多线程是线程池实现的,如果不当使用ThreadLocal, 由于线程重用,就会有该数据错乱问题。本篇博文会举例说明线程重用时,ThreadLocal变量会造成什么影响,并给出合适的解决方案。

案例说明

使用 Spring Boot 创建一个 Web 应用程序,使用 ThreadLocal 存放一个 Integer 的值,来暂且代表需要在线程中保存的用户信息,这个值初始是 null。
在业务逻辑中,我先从 ThreadLocal 获取一次值,然后把外部传入的参数设置到 ThreadLocal 中,来模拟从当前上下文获取到用户信息的逻辑,随后再获取一次值,最后输出两次获得的值和线程名称。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping(value = "threadlocal")
public class ThreadLocalTestClass {private ThreadLocal<Integer> currentUser =  ThreadLocal.withInitial(() -> null);@GetMapping(value = "wrong")public Map wrong(@RequestParam("id") Integer userId) {// 设置用户信息之前先查询一次ThreadLocal中的用户信息String before  = Thread.currentThread().getName() + ":" + currentUser.get();// 设置用户信息到ThreadLocalcurrentUser.set(userId);// 设置用户信息之后,再查询一次ThreadLocal中的用户信息String after  = Thread.currentThread().getName() + ":" + currentUser.get();// 汇总输出两次查询结果Map result = new HashMap();result.put("before", before);result.put("after", after);return result;  }
}

执行结果

图1

图2

图2的正确结果应该是 null, 2。但是此时却访问到了请求1的用户信息。并未实现请求1和请求2的隔离性!

执行结果说明

 * 以上方法中我们将tomcat的最大线程数设置为1。故只有一个线程在工作,所以线程会重用。* 由于我们使用了ThreadLocal, 每次请求完以后,线程中存放的ThreadLocal设置的值(本例是用户信息)依然存在。* 所以就可能导致另一个用户的请求打进来以后,从ThreadLocal中获取到的用户信息是上一个用户的信息。* 解决办法比较简单,就是在代码的 finally 代码块中,显式清除 ThreadLocal中的数据。* 这样一来,新的请求过来即使使用了之前的线程也不会获取到错误的用户信息了。

配置文件

正确的代码写法

 @GetMapping(value = "right")public Map wrong(@RequestParam("id") Integer userId) {// 设置用户信息之前先查询一次ThreadLocal中的用户信息String before  = Thread.currentThread().getName() + ":" + currentUser.get();// 设置用户信息到ThreadLocalcurrentUser.set(userId);// 设置用户信息之后,再查询一次ThreadLocal中的用户信息try {String after  = Thread.currentThread().getName() + ":" + currentUser.get();// 汇总输出两次查询结果Map result = new HashMap();result.put("before", before);result.put("after", after);return result;} finally {// 返回设置的新值以后,将Threadlocal中的用户信息清除,防止被下一个请求访问到,造成脏数据。currentUser.remove();}}

正确的结果


这样的话,即使线程重用了,每个Tomcat请求也不会访问到其他请求设置的值了,就不会出现数据错乱问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/795428.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows Server 2022 rdp

继续水一篇: 2022废弃了xddm转而使用wddm, rdp的渲染有比较大的变化。 高版本的unreal又需要2022支持,被迫走上魔改windows以提升2022 rdp环境下抓屏帧数的道路。 测试代码来自 https://github.com/robmikh/Win32CaptureSample ,只手动添加了输出fps逻辑。 patch windows后…

DuckDB简单使用及Python操作

DuckDB简介 DockUB官网DuckDB是一款开源免费类似Sqlite的嵌入式数据库,支持直接使用内存或单个文件作为数据库。 DuckDB着重于数据处理和分析,是一个款OLAP(联机分析处理)类型的数据库,主要特点如下:开源免费,MIT协议 功能完善,支持标准SQL、事务、二级索引等 高性能,…

CloudFire+PicGo搭建免费图床

目录CloudFire对象存储创建bucket配置域名配置 Bucket 访问 APIPicGO配置 CloudFire对象存储 | CloudFire提供对象存储服务,每个月有10G的免费额度,并且直连稳定可靠,无需CDN加速,足够作为日常图床使用。 创建bucket 打开CloudFire官网并注册账号,点击R2存储。创建图床buc…

Invicti v24.9.0 发布下载,新增功能概览

Invicti v24.9.0 发布下载,新增功能概览Invicti v24.9.0 for Windows - Web 应用程序安全测试 Invicti Standard v24.9.0 – 10 Sep 2024 请访问原文链接:https://sysin.org/blog/invicti/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.orgInvicti 是一种自动化…

Parallels Desktop 20 发布下载,macOS Sequoia 和 Windows 11 24H2 支持准备就绪

Parallels Desktop 20 发布下载,macOS Sequoia 和 Windows 11 24H2 支持准备就绪Parallels Desktop for Mac 20.0.0 (build 55653) - 在 Mac 上运行 Windows macOS Sequoia 和 Windows 11 24H2 支持准备就绪 请访问原文链接:https://sysin.org/blog/parallels-desktop/,查看…

rsync 学习笔记(一)编译

一、背景rsync二进制程序依赖外部库,由于安全问题,有时会单独升级依赖的外部库。另外为了防止因为栈溢出攻击导致服务器被黑,需要对rsync及其依赖的外部库重新编译,开启安全编译选项,增加黑客破解的复杂度。所有的库编译必须要求加上如下编译选项:栈保护(-fstack-protect…

Go runtime 调度器精讲(一):Go 程序初始化

原创文章,欢迎转载,转载请注明出处,谢谢。0. 前言 本系列将介绍 Go runtime 调度器。要学好 Go 语言,runtime 运行时是绕不过去的,它相当于一层“操作系统”对我们的程序做“各种类型”的处理。其中,调度器作为运行时的核心,是必须要了解的内容。本系列会结合 Go plan9 …

煤矿皮带跑偏监测识别系统

煤矿皮带跑偏监测识别系统对皮带状况进行实时监测,不用手动控制。一旦监测到皮带跑偏或者其他异常情况时,应该马上开展警报,通知监督管理办公室,并提醒负责人及时处置,并把警报截屏和视频储存到数据库系统系统中生成表格。煤矿皮带跑偏监测识别系统根据时间段对告警记录和…

GIS建库软件:地理信息与遥感领域的数据基石

在地理信息与遥感科学的浩瀚星海中,建库软件是构筑数据宇宙的基石,它不仅承载着海量地理空间数据,更是连接信息与应用的桥梁。本文将从其定义、关键技术、应用场景及未来发展四个方面,深入剖析建库软件如何支撑并推动整个行业的发展。建库软件:数据海洋的港湾建库软件,顾…

mysql月份前后格式变化记录

select DATE_FORMAT(2024-10-11 - INTERVAL 1 MONTH,%Y-%m) 一, DATE_FORMAT(2024-10-11,%Y-%m) 二, DATE_ADD(2024-10-11, INTERVAL 1 MONTH) 三 ,DATE_FORMAT(DATE_ADD(2024-10-11, INTERVAL 1 MONTH) ,%Y-%m) 四,REPLACE(DATE_FORMAT(DATE_ADD(2024-10-11, INTERVAL 1 MONT…

煤矿皮带撕裂检测系统

煤矿皮带撕裂检测系统可以全天候监管皮带的运送的工作情况,当煤矿皮带撕裂检测系统监管皮带撕裂时,马上停止皮带的运送,精准定位到皮带的裂开部位,工作员能够及时到现场维护保养。及时发现皮带撕裂,能够减少安全事故带来的损失,能够有效提升皮带机生产运输过程的效率。煤…

GIS数据采集软件:地理信息与遥感技术的智慧之眼

在信息时代,数据如同血液,滋养着各行各业的创新与进步,而地理信息与遥感领域中的数据采集软件,正是这股生命之源,它不仅为科学研究、城市规划、环境保护、灾害监测、资源管理等提供了精确数据支持,更是智慧城市建设的基石。本文将深入探讨其技术发展、应用及未来趋势,为…