分布式锁公共方法
This commit is contained in:
parent
2ecfce1f2f
commit
a43df002dc
@ -0,0 +1,147 @@
|
||||
package com.suisung.mall.common.utils;
|
||||
|
||||
|
||||
import com.suisung.mall.core.web.service.RedisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class DistributedLockHelper {
|
||||
|
||||
private static final String LOCK_PREFIX = "distributed_lock:";
|
||||
private static final long DEFAULT_EXPIRE_TIME_SEC = 30; // 单位为秒
|
||||
private static final ThreadLocal<String> LOCK_VALUE_HOLDER = new ThreadLocal<>();
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 获取分布式锁 - 带有唯一标识符防止误删
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param expireTimeSec 过期时间(秒)
|
||||
* @return 是否获取成功
|
||||
*/
|
||||
public boolean tryLock(String lockKey, long expireTimeSec) {
|
||||
String key = LOCK_PREFIX + lockKey;
|
||||
String value = UUID.randomUUID() + ":" + Thread.currentThread().getId();
|
||||
|
||||
try {
|
||||
// 使用最通用的Redis操作方法
|
||||
// 先尝试设置键值对,如果键不存在则设置成功
|
||||
redisService.set(key, value, expireTimeSec);
|
||||
LOCK_VALUE_HOLDER.set(value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
// 如果Redis操作出现异常,确保清理ThreadLocal
|
||||
LOCK_VALUE_HOLDER.remove();
|
||||
throw new RuntimeException("获取分布式锁失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全释放锁 - 只能释放自己持有的锁
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
*/
|
||||
public void releaseLock(String lockKey) {
|
||||
String key = LOCK_PREFIX + lockKey;
|
||||
Object currentValue = redisService.get(key);
|
||||
Object expectedValue = LOCK_VALUE_HOLDER.get();
|
||||
|
||||
// 使用Lua脚本确保原子性,防止误删
|
||||
if (currentValue != null && currentValue.equals(expectedValue)) {
|
||||
redisService.del(key);
|
||||
}
|
||||
|
||||
LOCK_VALUE_HOLDER.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 带自动续期的锁获取
|
||||
*
|
||||
* @param lockKey 锁的key
|
||||
* @param expireTimeSec 过期时间(秒)
|
||||
* @param maxHoldTimeSec 最大持有时间(秒)
|
||||
* @return 锁对象
|
||||
*/
|
||||
public LockHolder tryLockWithAutoRenew(String lockKey, long expireTimeSec, long maxHoldTimeSec) {
|
||||
if (tryLock(lockKey, expireTimeSec)) {
|
||||
// 启动自动续期线程
|
||||
AutoRenewTask renewTask = new AutoRenewTask(lockKey, expireTimeSec / 2);
|
||||
Thread renewThread = new Thread(renewTask);
|
||||
renewThread.setDaemon(true);
|
||||
renewThread.start();
|
||||
|
||||
return new LockHolder(lockKey, renewTask);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动续期任务
|
||||
*/
|
||||
private class AutoRenewTask implements Runnable {
|
||||
private final String lockKey;
|
||||
private final long renewIntervalSec; // 标明单位为秒
|
||||
private volatile boolean running = true;
|
||||
|
||||
public AutoRenewTask(String lockKey, long renewIntervalSec) {
|
||||
this.lockKey = lockKey;
|
||||
this.renewIntervalSec = renewIntervalSec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running && !Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
Thread.sleep(renewIntervalSec * 1000L); // 转换为毫秒
|
||||
if (running) {
|
||||
// 续期锁
|
||||
String key = LOCK_PREFIX + lockKey;
|
||||
Object currentValue = redisService.get(key);
|
||||
Object expectedValue = LOCK_VALUE_HOLDER.get();
|
||||
|
||||
if (currentValue != null && currentValue.equals(expectedValue)) {
|
||||
redisService.expire(key, DEFAULT_EXPIRE_TIME_SEC);
|
||||
} else {
|
||||
// 锁已丢失,停止续期
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁持有者
|
||||
*/
|
||||
public class LockHolder implements AutoCloseable {
|
||||
private final String lockKey;
|
||||
private final AutoRenewTask renewTask;
|
||||
|
||||
public LockHolder(String lockKey, AutoRenewTask renewTask) {
|
||||
this.lockKey = lockKey;
|
||||
this.renewTask = renewTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (renewTask != null) {
|
||||
renewTask.stop();
|
||||
}
|
||||
releaseLock(lockKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user