为什么不支持高并发呢,是我写的有问题吗?目前看来这个锁是没有办法锁住的
你的 key 是由什么组成?获得锁的结果是什么?
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
long now = System.currentTimeMillis();
// 尝试拿取本地锁
if (!this.localLock.tryLock(time, unit)) {
return false;
} else {
try {
long expire = now + TimeUnit.MILLISECONDS.convert(time, unit);
boolean acquired;
// 当前时间还没过期并且还未获得redis锁,睡眠100ms继续重试
while(!(acquired = this.obtainLock()) && System.currentTimeMillis() < expire) {
Thread.sleep(100L);
}
if (!acquired) {
this.localLock.unlock();
}
return acquired;
} catch (Exception var9) {
this.localLock.unlock();
this.rethrowAsLockException(var9);
return false;
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JgfSjKEd-1649006264961)(https://img2022.cnblogs.com/blog/1453965/202204/1453965-20220403172827774-1280476727.gif “点击并拖拽以移动”)]
问题分析: 根据参考资料,RedissonLock的tryLock方法可以保证分布式环境下对共享资源的同步访问,但是当使用tryLock方法时,可能出现并发访问的问题,导致无法锁住资源。问题可能是RedissonLock的tryLock方法本身存在问题,也有可能是代码实现错误。
解决方案: 1.检查代码实现,确保RedissonLock的tryLock方法被正确调用,并且锁的key值没有出现错误。 2.确认Redis版本是否支持分布式锁功能。 3.使用带唯一标识符的Redis分布式锁实现方法,可以通过UUID来为锁增加一个唯一标志。 4.使用定时器实现锁失效时间的续订,防止多线程访问时过期时间的冲突,并保证锁的正确性。 5.在判断获取锁的过程中,加入判断锁是否已经释放,以免锁已经被其它线程或进程释放而导致获取到不正确的锁状态。 6.对锁设置过期时间,并且在调用过程中加入阻塞,等待锁被释放之后再继续执行代码,以保证锁的使用正确性。
代码示例:
String uniqueId = UUID.randomUUID.toString() + threadId;
try{
String lockKey = "lockKey";
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,uniqueId, "hello",10,TimeUnit.SECONDS);
if(!result){
return "something exception";
}
// 大量业务代码,使用锁之后需要将锁释放,释放锁的代码如下
...
}
finally{
if(uniqueId.equals(redisTemplate.opsForValue().get(lockKey))){
redisTemplate. delete(lockKey);
}
}
// 定义定时器,30秒后对锁进行一次刷新,防止锁被竞争者抢占。
private static final ConcurrentMap<String, Timeout> expirationRenewalMap = PlatformDependent.newConcurrentHashMap();
private static long interval = 30000;
private void scheduleExpirationRenewal() {
// 保证任务不会被重复创建
if (expirationRenewalMap.containsKey(getName())) {
return;
}
// 添加一个netty的Timeout回调任务,每(interval / 3)毫秒执行一次
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
// 异步调用redis的pexpire命令,重置过期时间
expireAsync(lockExpirationTime, TimeUnit.MILLISECONDS);
// 移除,确保下一次调用
expirationRenewalMap.remove(getName());
// 再次循环调用
scheduleExpirationRenewal();
}
}, interval / 3, TimeUnit.MILLISECONDS);
// expirationRenewalMap如果已经有getName()任务,停止任务,确保不会重复创建
if (expirationRenewalMap.putIfAbsent(getName(), task) != null) {
task.cancel();
}
}