当同时存在三个线程访问同一个用户ID下订单的时候,
如果线程1发生了线程堵塞,互斥锁过期,同时还未执行释放互斥锁动作,
线程2开始执行业务存入互斥锁,在线程2执行任务期间,线程 1死灰复燃开始继续执行任务
,于是会发生线程1误操作将线程2的互斥锁删除的情况,
于是线程3在执行任务的时候,同样可以获取互斥锁。
解决问题思路:
在存入互斥锁时,key值使用的时不同订单的前缀+用户ID,value值可以用来判断
线程在释放锁时是否释放的时当前线程存入的锁,如果获取到的互斥锁值不同,
则不允许后台删除互斥锁,避免出现redis误删除情况的发生。
互斥锁的组成
key: 前缀+用户ID
value: 随机数(公共)+线程ID
package com.hmdp.utils;
import cn.hutool.core.util.BooleanUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class SimpleTimeOut implements ILock{
private StringRedisTemplate stringRedisTemplate;
//redis 的key值
private String name;
//redis 的前缀
private static final String KEY_PREFIX = "lock:";
//线程ID 的前缀
private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";
public SimpleTimeOut(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
/**
* 获取redis中的互斥锁
* @param timeOut
* @return
*/
@Override
public boolean tryLock(Long timeOut) {
//获取线程标识
String threadId = ID_PREFIX + Thread.currentThread().getId();
//存入redis
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeOut, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag);
}
/**
* 释放互斥锁
*/
@Override
public void unlock() {
//获取线程标识
String threadId = ID_PREFIX + Thread.currentThread().getId();
//获取redis中锁
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
//判断如果redis中的锁与当前线程标识相同允许删除
if(threadId.equals(id)){
stringRedisTemplate.delete(KEY_PREFIX + name);
}
}
}