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

设计一个利用事务特性可以阻塞线程的排他锁,并且通过注解和 AOP 来实现

设计思路:
利用数据库表记录锁标识:通过唯一标识符(如方法名 + 参数),我们可以在数据库中插入一条记录,表示当前方法正在执行。这条记录需要记录插入时间。
注解:通过注解标识哪些方法需要加锁,我们可以动态生成唯一标识符。
AOP:使用 AOP 切面处理方法调用逻辑,确保在进入目标方法之前判断是否已经有其他线程持有锁。如果有,则阻塞当前线程,直到锁被释放。
事务:利用事务的隔离性,保证在同一时刻,只有一个线程能够执行某个方法,其他线程需要等待。
超时检测:如果在某个时间点锁没有被释放(例如,锁存在时间超过10分钟),则认为发生了死锁,自动清理相关记录。
步骤及代码实现

  1. 数据库表设计
    首先,我们需要一个数据库表来存储锁的状态。表的结构大致如下:
CREATE TABLE method_lock (id BIGINT AUTO_INCREMENT PRIMARY KEY,lock_key VARCHAR(255) NOT NULL, -- 锁的唯一标识(方法名+参数)created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 锁的创建时间updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 最后更新时间status VARCHAR(50) DEFAULT 'LOCKED', -- 锁的状态,LOCKED 表示锁定,RELEASED 表示释放expired BOOLEAN DEFAULT FALSE -- 是否过期,10分钟未释放的锁算死锁
);
  1. 创建锁的注解
    接下来,我们定义一个注解来标识需要加锁的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLock {String value(); // 锁的标识(如方法名 + 参数的唯一标识符)
}
  1. 实现 AOP 切面
    在切面中,我们要通过 @Around 来拦截带有 @MethodLock 注解的方法,获取方法的唯一标识,并在数据库中插入或查询锁的状态。
@Aspect
@Component
public class MethodLockAspect {@Autowiredprivate LockRepository lockRepository; // 用于查询和操作数据库的 repository@Around("@annotation(methodLock)") // 拦截带有 @MethodLock 注解的方法public Object around(ProceedingJoinPoint joinPoint, MethodLock methodLock) throws Throwable {String lockKey = methodLock.value() + getMethodSignature(joinPoint); // 生成锁的唯一标识符// 获取当前时间戳long currentTime = System.currentTimeMillis();// 尝试获取锁if (tryAcquireLock(lockKey, currentTime)) {try {// 如果获取到锁,则执行目标方法return joinPoint.proceed();} finally {// 目标方法执行完毕后释放锁releaseLock(lockKey);}} else {// 如果无法获取到锁,则阻塞当前线程或者抛出异常throw new RuntimeException("Unable to acquire lock, try again later.");}}private boolean tryAcquireLock(String lockKey, long currentTime) {// 查询数据库中是否存在该锁记录LockEntity lockEntity = lockRepository.findByLockKey(lockKey);if (lockEntity == null) {// 如果不存在锁记录,则插入新的锁记录LockEntity newLockEntity = new LockEntity();newLockEntity.setLockKey(lockKey);newLockEntity.setCreatedTime(new Timestamp(currentTime));lockRepository.save(newLockEntity);return true;} else {// 如果锁存在,检查锁是否已经过期(超过10分钟)long lockAge = currentTime - lockEntity.getCreatedTime().getTime();if (lockAge > 10 * 60 * 1000) {// 如果锁超时超过10分钟,认为是死锁,清理并重新获取锁lockRepository.deleteByLockKey(lockKey); // 删除死锁记录LockEntity newLockEntity = new LockEntity();newLockEntity.setLockKey(lockKey);newLockEntity.setCreatedTime(new Timestamp(currentTime));lockRepository.save(newLockEntity);return true;}// 如果锁没有过期,则阻塞当前线程return false;}}private void releaseLock(String lockKey) {// 释放锁:删除数据库中的锁记录lockRepository.deleteByLockKey(lockKey);}private String getMethodSignature(ProceedingJoinPoint joinPoint) {// 获取方法签名(例如方法名 + 参数)String methodName = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();StringBuilder signature = new StringBuilder(methodName);for (Object arg : args) {signature.append("-").append(arg != null ? arg.toString() : "null");}return signature.toString();}
}
  1. LockRepository 示例
    假设使用的是 Spring Data JPA 来进行数据库操作,我们可以定义一个简单的 Repository 来操作 method_lock 表。
public interface LockRepository extends JpaRepository<LockEntity, Long> {LockEntity findByLockKey(String lockKey);void deleteByLockKey(String lockKey);
}
  1. LockEntity 实体类
    LockEntity 对应数据库中的 method_lock 表:
@Entity
@Table(name = "method_lock")
public class LockEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String lockKey;@Column(name = "created_time")private Timestamp createdTime;@Column(name = "updated_time")private Timestamp updatedTime;private String status;private Boolean expired;// Getters and Setters
}
  1. 使用示例
    现在,我们可以在需要加锁的方法上使用 @MethodLock 注解,示例如下:
@Service
public class MyService {@MethodLock("uniqueLockKey")@Transactionalpublic void executeTask(String param) {// 执行需要加锁的逻辑System.out.println("Executing task with param: " + param);}
}

关键点总结:
锁的唯一标识:通过方法名和参数生成一个唯一的标识符 lockKey。
数据库表记录锁:通过表记录锁的状态,保证只有一个线程能获取到锁。
AOP 和注解结合:使用 AOP 和注解对方法进行拦截,判断是否已经有其他线程在执行,若没有则获取锁,若有则阻塞或抛出异常。
事务特性:保证同一时刻,只有一个线程能够执行目标方法,其他线程需要等待,。
死锁处理:在锁存在超过 10 分钟时认为是死锁,进行清理。
通过这种方式,可以实现基于事务的排他锁,确保多个线程访问同一资源时进行排队执行

解释:
例如,多个接口调用一个方法,要求多个线程调用该方法时,这些线程排队执行,该方法使用注解,aop切面,通过该注解上的特定字符串和方法名,组成唯一标识,将该标识插入表中,并且记录插入时间,并且该标识是唯一的,当有线程调用该方法时,先组装唯一标识,查询表中是否有该标识的数据,并判断是否时间距离现在是否超过了10分钟,如果超过10分钟就是死锁了,直接删除该标识的所有记录,如果没有查到就插入一条记录,如果该方法没有执行完,就在一个事务里面,该事务没有结束,当有其他方法调用该方法时,就回去查询并插入,由于前一个事务未结束,导致当前唯一标识一致,卡在插入数据时,从而达到阻塞的目的,最后方法结束后将该数据删除。

相关文章:

设计一个利用事务特性可以阻塞线程的排他锁,并且通过注解和 AOP 来实现

设计思路&#xff1a; 利用数据库表记录锁标识&#xff1a;通过唯一标识符&#xff08;如方法名 参数&#xff09;&#xff0c;我们可以在数据库中插入一条记录&#xff0c;表示当前方法正在执行。这条记录需要记录插入时间。 注解&#xff1a;通过注解标识哪些方法需要加锁&a…...

【2024年华为OD机试】 (A卷,100分)- 对称美学(Java JS PythonC/C++)

一、问题描述 题目描述 对称就是最大的美学&#xff0c;现有一道关于对称字符串的美学。已知&#xff1a; 第1个字符串&#xff1a;R第2个字符串&#xff1a;BR第3个字符串&#xff1a;RBBR第4个字符串&#xff1a;BRRBRBBR第5个字符串&#xff1a;RBBRBRRBBRRBRBBR 相信你…...

【教程】数据可视化处理之2024年各省GDP排名预测!

过去的一年里&#xff0c;我国的综合实力显著提升&#xff0c;在新能源汽车、新一代战机、两栖攻击舰、航空航天、芯片电子、装备制造等领域位居全球前列。虽然全国各省市全年的经济数据公布还需要一段时间&#xff0c;但各地的工业发展数据&#xff0c;财政收入数据已大概揭晓…...

Java 将RTF文档转换为Word、PDF、HTML、图片

RTF文档因其跨平台兼容性而广泛使用&#xff0c;但有时在不同的应用场景可能需要特定的文档格式。例如&#xff0c;Word文档适合编辑和协作&#xff0c;PDF文档适合打印和分发&#xff0c;HTML文档适合在线展示&#xff0c;图片格式则适合社交媒体分享。因此我们可能会需要将RT…...

深度学习的原理和应用

一、深度学习的原理 深度学习是机器学习领域的一个重要分支&#xff0c;其原理基于多层神经网络结构和优化算法。以下是深度学习的核心原理&#xff1a; 多层神经网络结构&#xff1a;深度学习模型通常由多层神经元组成&#xff0c;这些神经元通过权重和偏置相互连接。输入数据…...

CAPL语法基础

CAPL语法基础 目录 CAPL语法基础1. 引言2. 数据类型、变量与常量2.1 数据类型2.2 变量2.3 常量2.4 案例1&#xff1a;使用变量和常量计算圆的面积 3. 运算符与表达式3.1 算术运算符3.2 关系运算符3.3 逻辑运算符3.4 位运算符3.5 案例2&#xff1a;使用运算符实现简单的逻辑判断…...

安卓studio生成apk步骤

在写完app之后虽然能在真机上运行 但是在文件夹中找不到相应的apk &#xff0c;注意&#xff01;&#xff01;&#xff01;安卓 studio中可以自动生动生成 apk 下面是生成步骤&#xff1a; 步骤1&#xff1a;build ->make project 步骤2&#xff1a;build ->Generate si…...

Azure主机windows2008就地升级十步

Azure上云主机的windows2008系统需要进行就地升级。 按着微软的升级路径&#xff1a;win2008-->win2012-->win2016-->win2022 第一步&#xff1a;创建快照备份&#xff0c;防止升级失败第二步&#xff1a;升级托管磁盘&#xff0c;在VM管理的地方将磁盘升级成托管磁盘…...

解锁 C# 与 LiteDB 嵌入式 NoSQL 数据库

一、开篇&#xff1a;邂逅 C# 与 LiteDB 新世界 在当今的软件开发领域&#xff0c;数据管理如同建筑的基石&#xff0c;而选择一款合适的数据库则是项目成功与否的关键因素之一。对于 C# 开发者来说&#xff0c;面对琳琅满目的数据库选项&#xff0c;如何抉择常常令人头疼。今…...

7 分布式定时任务调度框架

先简单介绍下分布式定时任务调度框架的使用场景和功能和架构&#xff0c;然后再介绍世面上常见的产品 我们在大型的复杂的系统下&#xff0c;会有大量的跑批&#xff0c;定时任务的功能&#xff0c;如果在独立的子项目中单独去处理这些任务&#xff0c;随着业务的复杂度的提高…...

七星棋类游戏源码:两百玩法开源修复

这套七星棋类源码&#xff0c;覆盖六大省区&#xff08;湖南双端、湖北、山西、江苏、贵州等&#xff09;&#xff0c;安卓与苹果端都能轻松适配&#xff0c;汇集 6 个端口与 200 多种子游戏玩法。此版本为二次开发修复版&#xff0c;功能完备且源码完全公开&#xff0c;包括乐…...

未来世界:科技引领的奇幻篇章

科技发展的这么快&#xff0c;未来的世界将会是什么样的呢&#xff1f; 在人类历史的长河中&#xff0c;科技始终是推动社会进步的核心力量。从古老的四大发明到如今的人工智能、基因编辑、量子计算等前沿技术&#xff0c;科技发展的速度日新月异。我们不禁会想&#xff0c;在…...

[python3]Uvicorn库

Uvicorn 是一个用于运行 ASGI&#xff08;Asynchronous Server Gateway Interface&#xff09;应用程序的轻量级服务器。ASGI 是 Python Web 应用程序接口的一种扩展&#xff0c;它不仅支持传统的同步 Web 请求处理&#xff0c;还支持异步请求处理、WebSockets 以及 HTTP/2。 h…...

istio-proxy oom问题排查步骤

1. 查看cluster数量 cluster数量太多会导致istio-proxy占用比较大的内存&#xff0c;此时需检查是否dr资源的host设置有配置为* 2. 查看链路数据采样率 若采样率设置过高&#xff0c;在压测时需要很大的内存来维护链路数据。可以调低采样率或增大istio-proxy内存。 检查iop中…...

Flutter:使用FVM安装多个Flutter SDK 版本和使用教程

一、FVM简介 FVM全称&#xff1a;Flutter Version Management FVM通过引用每个项目使用的Flutter SDK版本来帮助实现一致的应用程序构建。它还允许您安装多个Flutter版本&#xff0c;以快速验证和测试您的应用程序即将发布的Flutter版本&#xff0c;而无需每次等待Flutter安装。…...

关于物联网的基础知识(二)——物联网体系结构分层

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网的基础知识&#xff08;二&a…...

[程序设计]—代理模式

[程序设计]—代理模式&#x1f473; 本文章记录学习于——52.面向切面&#xff1a;AOP-场景模拟_哔哩哔哩_bilibili 最近闲来无事&#xff0c;在学习Spring的源码&#xff1a; 后面慢慢更新源码系列blog&#xff0c;希望多多关注&#x1f64f;&#x1f64f; 目前已经总结的b…...

26、【OS】【Nuttx】用cmake构建工程

背景 之前wiki 14、【OS】【Nuttx】Nsh中运行第一个程序 都是用 make 构建&#xff0c;准备切换 cmake 进行构建&#xff0c;方便后续扩展开发 Nuttx cmake 适配 nuttx项目路径下输入 make distclean&#xff0c;清除之前工程配置 adminpcadminpc:~/nuttx_pdt/nuttx$ make …...

C#中序列化的选择:JSON、XML、二进制与Protobuf详解

C#中序列化的选择&#xff1a;JSON、XML、二进制与Protobuf详解 在C#开发中&#xff0c;序列化是将对象转换为可存储或传输的格式的过程&#xff0c;而反序列化则是将存储或传输的数据重新转换为对象的过程。选择合适的序列化方式对应用程序的性能、可维护性和兼容性至关重要。…...

单片机实现模式转换

[任务] 要求通过单片机实现以下功能&#xff1a; 1.单片机有三种工作模式(定义全局变量MM表示模式&#xff0c;MM1&#xff0c;2&#xff0c;3表示三种不同的模式) LED控制模式 风扇控制模式 蜂鸣器控制模式 2.可以在某一个模式下通过拓展板KEY1按键控制设备 (按…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...