ThreadLocal加切面实现线程级别的方法缓存
1、实现效果
当一个请求线程多次请求A方法时,只会触发一次A方法的实际调用,会将方法结果缓存起来,避免多次调用。
2、实现过程
1. 需要一个注解ThreadLocalCache,在需要缓存的方法上加上该注解
2. 需要一个切面,借助ThreadLocal,将结果缓存起来,利用环绕通知来实现方法拦截从缓存中返回方法执行结果
3、代码实现
3.1、ThreadLocalCache注解创建
作用于方法级别
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadLocalCache {
}
3.2、ThreadLocalTestAspect切面创建
@Aspect
@Component
public class ThreadLocalTestAspect {private ThreadLocal<Map<Object, Object>> threadLocal = new ThreadLocal<>();@Around("@annotation(com.example.test.ThreadLocalCache)")private Object myPointcut(ProceedingJoinPoint proceedingJoinPoint) {//获取方法的入参Object[] args = proceedingJoinPoint.getArgs();Signature signature = proceedingJoinPoint.getSignature();//获取目标方法名String name = signature.getName();//获取目标方法的类的完全限定名String declaringTypeName = signature.getDeclaringTypeName();//生成缓存keyObject key = SimpleKeyGenerator.generateKey(args, declaringTypeName, name);if (Objects.isNull(threadLocal.get())) {threadLocal.set(new HashMap<>(8));}try {if (!threadLocal.get().containsKey(key)) {threadLocal.get().put(key, proceedingJoinPoint.proceed());}} catch (Throwable e) {//日志记录e.printStackTrace();}return threadLocal.get().get(key);}public void removeThreadLocal(){threadLocal.remove();}}
4、测试过程
- 创建一个接口及实现
public interface ThreadLocalTestService {Long getParentIdByName(String name);
}
@Service
public class ThreadLocalTestServiceImpl implements ThreadLocalTestService{@ThreadLocalCache@Overridepublic Long getParentIdByName(String name) {//根据name查询父级IDSystem.out.println("com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了");return 666L;}
}
- 方法调用
@RestController
@RequestMapping("/ThreadLocalTest")
public class ThreadLocalTest {@Autowiredprivate ThreadLocalTestService threadLocalTestService;@Autowiredprivate ThreadLocalTestAspect threadLocalTestAspect;@GetMapping("getParentIdByName")public Long getParentIdByName(String name){System.out.println(Thread.currentThread().getName());threadLocalTestService.getParentIdByName(name);threadLocalTestService.getParentIdByName(name);Long parentId = threadLocalTestService.getParentIdByName(name);threadLocalTestAspect.removeThreadLocal();return parentId;}}
3.执行结果
http-nio-8087-exec-1
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-2
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-4
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-5
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-6
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-7
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-8
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-10
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-9
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-3
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-1
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-2
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-是线程名字,可以看到http-nio-8087-exec-1执行了两次,每次都调用四次getParentIdByName 方法,但getParentIdByName 方法实际至执行了一次,剩下的三次是从缓存中获取的。
这里需要注意的是:线程每次结束的时候都需要调用threadLocalTestAspect.removeThreadLocal();为的是把当前线程threadLocal里的缓存抹掉,因为同一个线程可能会被重复使用,所以不抹掉,可能会导致多次请求使用同一个线程,目标方法只会执行一次,和我们的最初的实现效果是违背的。
下面是不调用threadLocalTestAspect.removeThreadLocal();的执行结果
http-nio-8087-exec-1
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-3
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-8
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-5
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-6
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-7
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-4
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-9
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-10
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-2
com.example.test.ThreadLocalTestServiceImpl.getParentIdByName 执行了
http-nio-8087-exec-1
http-nio-8087-exec-3
http-nio-8087-exec-8
http-nio-8087-exec-5
可以很清楚的看到http-nio-8087-exec-1、3、5、8再次请求的时候getParentIdByName 方法并没有执行了,因为之前的threadlocal缓存没有被remove导致的。
相关文章:
ThreadLocal加切面实现线程级别的方法缓存
1、实现效果 当一个请求线程多次请求A方法时,只会触发一次A方法的实际调用,会将方法结果缓存起来,避免多次调用。 2、实现过程 1. 需要一个注解ThreadLocalCache,在需要缓存的方法上加上该注解 2. 需要一个切面,借助ThreadLocal,将结果缓存起来,利用环绕通知来实现方法拦截从…...

使用 Flume 将 CSV 数据导入 Kafka:实现实时数据流
使用 Flume 将 CSV 数据导入 Kafka:实现实时数据流 文介绍了如何使用 Apache Flume 将 CSV 格式的数据从本地文件系统导入到 Apache Kafka 中,以实现实时数据流处理。通过 Flume 的配置和操作步骤,我们可以轻松地将数据从 CSV 文件中读取并发…...

对代理模式的理解
目录 一、前言二、案例1 代码2 自定义代理类【静态代理】2.1 一个接口多个实现,到底注入哪个依赖呢?2.1.1 Primary注解2.1.2 Resource注解(指定name属性)2.1.3 Qualifier注解 2.2 面向接口编程2.3 如果没接口咋办呢?2.…...

#QT项目实战(天气预报)
1.IDE:QTCreator 2.实验: 3.记录: (1)调用API的Url a.调用API获取IP whois.pconline.com.cn/ipJson.jsp?iphttp://whois.pconline.com.cn/ipJson.jsp?ip if(window.IPCallBack) {IPCallBack({"ip":&quo…...

数据挖掘|关联分析与Apriori算法详解
数据挖掘|关联分析与Apriori算法 1. 关联分析2. 关联规则相关概念2.1 项目2.2 事务2.3 项目集2.4 频繁项目集2.5 支持度2.6 置信度2.7 提升度2.8 强关联规则2.9 关联规则的分类 3. Apriori算法3.1 Apriori算法的Python实现3.2 基于mlxtend库的Apriori算法的Python实现 1. 关联分…...
ChatGPT Excel 大师
原文:ChatGPT Excel Mastery 译者:飞龙 协议:CC BY-NC-SA 4.0 序言 欢迎来到 Excel 掌握的变革之旅,在这里,尖端技术和永恒专业知识在“ChatGPT Excel 掌握:释放专家技巧和窍门的力量”中融合。在当今快节…...
C 语言中的 end, _end 符号
使用 man 3 end 可以看到相关符号的解释 这些符号不是在 C 语言文件和头文件中定义的,它们是 ld 在链接所有 .o 文件的时候自己添加的。 end 和 _end 的地址,就是最终程序的堆的起始地址 要打印它们的话,一个样例程序在下面: …...

绿联 安装PDF工具
这是一个强大的本地托管的基于 Web 的 PDF 操作工具,使用 docker,允许您对 PDF 文件执行各种操作,例如拆分、合并、转换、重组、添加图像、旋转、压缩等。这个本地托管的 Web 应用程序最初是 100% ChatGPT 制作的应用程序,现已发展…...

备战蓝桥杯---数论相关问题
目录 一、最大公约数和最小公倍数 二、素数判断 三、同余 四、唯一分解定理 五、约数个数定理 六、约数和定理 五、快速幂 六、费马小定理 七、逆元 一、最大公约数和最小公倍数 文章链接:最大公约数和最小公倍数 二、素数判断 文章链接:在J…...

苹果手表Apple Watch录了两个半小时的录音,却只能播放4秒,同步到手机也一样,还能修复好吗?
好多人遇到这个情况,用苹果手表Apple Watch录音,有的录1个多小时,有的录了3、4小时,甚至更长时间,因为手表没电,忘记保存等原因造成录音损坏,都是只能播放4秒,同步到手机也一样&…...

RGB三通道和灰度值的理解
本文都是来自于chatGPT的回答!!! 目录 Q1:像素具有什么属性?Q2:图像的色彩是怎么实现的?Q3:灰度值和颜色值是一个概念吗?Q4:是不是像素具有灰度值,也有三个颜色分量RGB?Q5:灰度图像是没有色彩的吗?Q6: 彩色图像是既具有灰度值也具有RGB三…...

ARM、X86、RISC-V三分天下
引入: 简单的介绍一下X86、ARM、RISC-V三种cpu架构的区别和应用场景。 目录 简单概念讲解 1. X86架构 2. ARM架构 3. RISC-V架构 应用场景 X86、ARM和RISC-V是三种不同的CPU架构,它们在设计理念、指令集和应用场景上有一些区别。 简单概念讲解 1. X…...

力控机器人原理及力控制实现
力控机器人原理及力控制实现 力控机器人是一种能够感知力量并具有实时控制能力的机器人系统。它们可以在与人类进行精准协作和合作时,将力传感技术(Force Sensing Technology)和控制算法(Control Algorithm)结合起来&a…...
最小生成树
最小生成树问题是指给定一个带权的无向图,删除一些边使得这个无向图变成一棵树,并且权值之和最小。 解决此类问题的方法主要有两种:Prim算法,Kruskal算法 Prim 算法 从一个点开始,逐步扩展,每次选择权值…...

二维动画制作软件 Animate 2024 for mac激活版
Animate 2024 for Mac是一款功能强大的二维动画制作软件,专为Mac用户打造。它提供了丰富的动画编辑功能,使用户能够轻松创建出生动逼真的动画作品。无论是短片、广告还是游戏等应用领域,Animate 2024都能发挥出出色的表现。 软件下载…...
相对论中关于光速不变理解的补充
近几个月在物理直播间聊爱因斯坦相对论,发现好多人在理解爱因斯坦相对论关于基本假设,普遍认为光速是不变的,质能方程 中光速的光速不变的,在这里我对这个假设需要做一个补充,他是基于质能方程将光速C 在真是光速变化曲…...

面试(04)————JavaWeb
1、网络通讯部分 1.1、 TCP 与 UDP 区别? 1.2、什么是 HTTP 协议? 1.3、TCP 的三次握手,为什么? 1.4、HTTP 中重定向和请求转发的区别? 1.5、 Get 和 Post 的区别? 2、cookie 和 session 的区别&am…...

Debian12 使用 nginx 与 php8.2 使用 Nextcloud
最近将小服务器升级了下系统,使用了 debian12 的版本,正好试试 nginx 和 php-fpm 这种方式运行 Nextcloud 这个私有云的配置。 一、基本系统及应用安装 系统:debian12 x86_64 位版本最小安装,安装后可根据自己需求安装一些工具&…...

Java设计模式:代理模式的静态和动态之分(八)
码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 ! 在软件设计中,代理模式是一种常用的设计模式,它为我们提供了一种方式来控制对原始对象的访问。在Java中&a…...

【论文通读】AgentStudio: A Toolkit for Building General Virtual Agents
AgentStudio: A Toolkit for Building General Virtual Agents 前言AbstractMotivationFramework评估GUI GroudingReal-World Cross-Application Benchmark Suite Conclusion 前言 来自昆仑万维的一篇智能体环境数据大一统框架工作,对未来计算机智能体的发展具有指…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...