分布式锁特征

  1. 互斥性

    任意时刻,只有一个客户端能持有锁

  2. 锁超时释放

    持有锁超时,可以释放,防止死锁

  3. 可重入性

    一个线程获取锁之后,可以再次对其请求加锁

  4. 高可用,高性能

    加锁和解锁需要开销尽可能低,同时也要保证高可用

  5. 安全性

    锁只能被持有的客户端删除,不能被其他客户端删除


锁满足条件

  • 互斥性。在任意时刻,只有一个客户端能持有锁。
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  • 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

代码实现

首先导入spring-data-redis依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Controller

LockController.java(模拟扣库存的高并发情景)

package com.lyx.redis02.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
* @author Steven0516
* @create 2021-08-16
*/

@Controller
public class LockController {
@Autowired
private StringRedisTemplate stringRedisTemplate;


// 模拟高并发抢购商品的情景
@RequestMapping("/reduce_product")
@ResponseBody
public String reduce_product() {
String lockKey = "product_001"; //商品编号

//加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。相当于个每个客户生成一个客户端号
String clientId = UUID.randomUUID().toString();



//捕获异常的目的是防止程序运行中间有异常 而导致解锁不了的问题
try {
Boolean res = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);
//若redis中已经包含该锁,则返回错误(前端显示类似系统繁忙,请稍后重试)
if(!res){
return "error";
}

//从redis中获取库存(stock)
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));

if (stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功!剩余库存:" + realStock);
}else{
System.out.println("扣减失败!库存不足");
}
} finally {
if(stringRedisTemplate.opsForValue().get(lockKey).equals(clientId)){
stringRedisTemplate.delete(lockKey);
}
}
return "end";
}
}

访问

在浏览器输入localhost:8080/reduce_product

若库存成功扣减,则

fW9DED.md.png

运行结果

fW9WKP.md.png