分布式锁特征
互斥性
任意时刻,只有一个客户端能持有锁
锁超时释放
持有锁超时,可以释放,防止死锁
可重入性
一个线程获取锁之后,可以再次对其请求加锁
高可用,高性能
加锁和解锁需要开销尽可能低,同时也要保证高可用
安全性
锁只能被持有的客户端删除,不能被其他客户端删除
锁满足条件
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
- 具有容错性。只要大部分的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;
@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); if(!res){ return "error"; }
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
若库存成功扣减,则
运行结果