1.分布式锁实现
上锁:setnx key value
解锁:del key
超时机制:set key value nx ex time
基于此思路实现伪代码:
java
public class RedisLock{
@Autowired
private static RedisTemplate rt;
private static final LOCK_WORD = "lock";
private static final int TIME_OUT = 3;
public void tryLock(String uuid ){
//String uuid = UUID.get();
boolean lock = rt.setnx(LOCK_WORD,uuid,TIME_OUT,TimeUnit.SECOND);
if(lock){ // 没有考虑可重入锁设计
try{
do(业务逻辑)
}finally{ // 需要保证原子操作
String tempLock = rt.get(LOCK_WORD);
if(uuid.equals(tempLock)){
rt.del(LOCK_WORD);
}
}
}else {
try{
Thread.sleep(100);
tryLock(); // 递归调用可能会爆栈,应该实现一个等待队列
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
实际实现代码:
java
@GetMapping("testLock")
public void testLock() {
// 1 获取锁,setIfAbsent,同时设置3秒过期,以避免中间出现异常,导致锁一直无法释放
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111", 3, TimeUnit.SECONDS);
// 2 获取锁成功,查询num的值
if (lock) {
Object value = redisTemplate.opsForValue().get("num");
// 2.1 判断num为空,return
if (StringUtils.isEmpty(value)) {
return;
}
// 2.2 有值就转成int
int num = Integer.parseInt(value + "");
// 2.3 把redis的num加1
redisTemplate.opsForValue().set("num", ++num);
// 2.4 释放锁,删除lock
String lockUuid = (String) redisTemplate.opsForValue().get("lock");
if ("111".equals(lockUuid)) {
redisTemplate.delete("lock");
}
} else {
// 3 获取锁失败,每隔0.1秒再获取
try {
Thread.sleep(100);
testLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.秒杀场景(解决超卖)
思路:
- 维护一个用户 id,商品 id,库存信息,秒杀成功名单
- 先确认库存正常,即秒杀开始
- 先查询 uid 是否在成功名单,避免重复秒杀
- 再查询库存是否充足,
- 利用事务完成: 扣除库存、添加名单,秒杀成功
伪代码:
java
@Autowired
private RedisTemplate rt;
public boolean doSecondKill(String uid,String productId){
String successListKey = "User-"+productId;
String invenKey = "Inventory-"+productId;
if(rt.get(invenKey)==null){
print("秒杀尚未开启");
return false;
}
if(rt.sismember(successListKey,uid)){
print("不可重复秒杀");
return false;
}
if(Integer.parseInt(rt.get(invenKey))<=0){
print("秒杀已结束");
return false;
}
Transaction multi = rt.multi();
multi.decr(invenKey); // 扣除库存
multi.sadd(successListKey,uid); // 添加成功秒杀
List<Object> result = multi.exec();
if(result==null || result.size()==0){
print("秒杀失败");
return false;
}
print("秒杀成功");
return true;
}
代码实现
java
public class SecKill {
public static void main(String[] args) {
Jedis jedis = new Jedis();
}
public static boolean doSecKill(String uid, String proid) throws IOException {
// 1.uid和proid判断
if (uid == null || proid == null) {
return false;
}
// 2.连接jedis,使用连接池,避免超时
JedisPool jedisPool = new JedisPool();
Jedis jedis = jedisPool.getResource();
// 3. 拼接key
String invenKey = "sk:" + proid + "qt";
String userKey = "sk:" + proid + "qt";
// 监视库存
jedis.watch(invenKey);
// 4. 判断是否开始
String inven = jedis.get(invenKey);
if (inven == null) {
System.out.println("秒杀尚未开始");
jedis.close();
return false;
}
// 5.判断是否抢过
if (jedis.sismember(userKey, uid)) {
System.out.println("不可重复参与!");
jedis.close();
return false;
}
// 6.判断库存是否充足
if (Integer.parseInt(jedis.get(invenKey)) <= 0) {
System.out.println("秒杀已结束,失败");
jedis.close();
return false;
}
// multi事务操作
Transaction multi = jedis.multi();
multi.decr(invenKey);
multi.sadd(userKey, uid);
List<Object> result = multi.exec();
if (result == null || result.size() == 0) {
System.out.println("秒杀失败");
jedis.close();
return false;
}
System.out.println("秒杀成功");
jedis.close();
return true;
}
}