网站开发作业,网站开发的两种模式,做的网站上传到服务器,宁波建设协会网站设计思路#xff1a; 利用数据库表记录锁标识#xff1a;通过唯一标识符#xff08;如方法名 参数#xff09;#xff0c;我们可以在数据库中插入一条记录#xff0c;表示当前方法正在执行。这条记录需要记录插入时间。 注解#xff1a;通过注解标识哪些方法需要加锁 利用数据库表记录锁标识通过唯一标识符如方法名 参数我们可以在数据库中插入一条记录表示当前方法正在执行。这条记录需要记录插入时间。 注解通过注解标识哪些方法需要加锁我们可以动态生成唯一标识符。 AOP使用 AOP 切面处理方法调用逻辑确保在进入目标方法之前判断是否已经有其他线程持有锁。如果有则阻塞当前线程直到锁被释放。 事务利用事务的隔离性保证在同一时刻只有一个线程能够执行某个方法其他线程需要等待。 超时检测如果在某个时间点锁没有被释放例如锁存在时间超过10分钟则认为发生了死锁自动清理相关记录。 步骤及代码实现
数据库表设计 首先我们需要一个数据库表来存储锁的状态。表的结构大致如下
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分钟未释放的锁算死锁
);
创建锁的注解 接下来我们定义一个注解来标识需要加锁的方法。
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface MethodLock {String value(); // 锁的标识如方法名 参数的唯一标识符
}
实现 AOP 切面 在切面中我们要通过 Around 来拦截带有 MethodLock 注解的方法获取方法的唯一标识并在数据库中插入或查询锁的状态。
Aspect
Component
public class MethodLockAspect {Autowiredprivate LockRepository lockRepository; // 用于查询和操作数据库的 repositoryAround(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();}
}
LockRepository 示例 假设使用的是 Spring Data JPA 来进行数据库操作我们可以定义一个简单的 Repository 来操作 method_lock 表。
public interface LockRepository extends JpaRepositoryLockEntity, Long {LockEntity findByLockKey(String lockKey);void deleteByLockKey(String lockKey);
}
LockEntity 实体类 LockEntity 对应数据库中的 method_lock 表
Entity
Table(name method_lock)
public class LockEntity {IdGeneratedValue(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
}
使用示例 现在我们可以在需要加锁的方法上使用 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分钟就是死锁了直接删除该标识的所有记录如果没有查到就插入一条记录如果该方法没有执行完就在一个事务里面该事务没有结束当有其他方法调用该方法时就回去查询并插入由于前一个事务未结束导致当前唯一标识一致卡在插入数据时从而达到阻塞的目的最后方法结束后将该数据删除。