当前位置:网站首页>如何用 Redis 实现一个分布式锁
如何用 Redis 实现一个分布式锁
2022-06-23 11:02:00 【InfoQ】
场景模拟

@RestController
public class SkillController {
@Autowired
private RedisTemplate redisTemplate;
// 秒杀接口
@RequestMapping("/deduct_stock")
public String deductStock() {
// 加锁
synchronized (this) {
int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock").toString());
if (stock > 0) {
// 库存 -1
int realStock = stock - 1;
// 扣减库存
redisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
}
return "8080";
}
}
@RestController
public class SkillController {
@Autowired
private RedisTemplate redisTemplate;
// 秒杀接口
@RequestMapping("/deduct_stock")
public String deductStock() {
// 加锁
synchronized (this) {
int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock").toString());
if (stock > 0) {
// 库存 -1
int realStock = stock - 1;
// 扣减库存
redisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
}
return "8090";
}
}


JVM 锁



Redis SETNX
@RestController
public class SkillController {
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock() {
// 商品 ID,具体应用中应该是请求传入的
String lockKey = "lock:product_01";
// SETNX 加锁
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "product");
// 如果为 false 说明这把锁存在,直接返回
if (!result) {
// 模拟返回业务
return "系统繁忙";
}
int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock").toString());
if (stock > 0) {
// 库存 -1
int realStock = stock - 1;
// 扣减库存 模拟其他更多业务操作
redisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
// 加锁后需要释放锁
redisTemplate.delete(lockKey);
return "8080";
}
}try catchtry finally
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "product");
redisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, "product", 10, TimeUnit.SECONDS);
Redisson
@RestController
public class RedissonController {
@Autowired
private Redisson redisson;
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("/deduct_stock1")
public String deductStock() {
// 商品 ID,具体应用中应该是请求传入的
String lockKey = "lock:product_01";
// 获取锁
RLock lock = redisson.getLock(lockKey);
// 加锁
lock.lock();
try {
int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock").toString());
if (stock > 0) {
// 库存 -1
int realStock = stock - 1;
// 扣减库存
redisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
// 释放锁
lock.unlock();
}
return "8080";
}
}

lualuaRedissonLock.lock()--->lockInterruptibly()--->tryAcquire()--->tryLockInnerAsync()tryLockInnerAsync()<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
// 如果锁不存在,则通过hset设置它的值,并设置过期时间
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 如果锁已存在,其是当前线程,则通过hincrby给数值递增1,即锁的重入
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 如果锁已存在,不是当前线程,则返回过期时间 ttl
"return redis.call('pttl', KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}RedissonLock.lock()--->lockInterruptibly()--->tryAcquire()--->scheduleExpirationRenewal()scheduleExpirationRenewal()lua// getName()就是当前锁的名字
RFuture<Boolean> future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
// 判断这个锁 getName() 是否在redis中存在,如果存在就进行 pexpire 延期 默认30s
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return 1; " +
"end; " +
"return 0;",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
getName()pexpirelockWatchdogTimeout=30slockWatchdogTimeout/3=10s边栏推荐
- [use of Matlab graphical user interface controls]
- Esp32-cam, esp8266, WiFi, Bluetooth, MCU, hotspot create embedded DNS server
- NOI OJ 1.3 17:计算三角形面积 C语言
- Argmax function notes - full of details
- NOI OJ 1.3 05:计算分数的浮点数值 C语言
- What is a good quick development framework like?
- vector的介绍及使用
- 最简单DIY基于51单片机、PCA9685、IIC、云台的舵机集群控制程序
- TTY drive frame
- Unity technical manual - shape sub module - Sprite, spriterenderer and velocity over lifetime
猜你喜欢

Simplest DIY mpu6050 gyroscope attitude control actuator program based on stm32f407 Explorer development board
[Architect (Part 40)] connecting mongodb database developed by server

C语言结构体字节对齐问题

最简单DIY串口蓝牙硬件实现方案

图片存储--引用

Simplest DIY remote control computer system based on STM32 ① (capacitive touch + key control)

当 Pandas 遇见 SQL,一个强大的工具库诞生了

The problem of C language structure byte alignment

最简单DIY基于STM32的远程控制电脑系统①(电容触摸+按键控制)

What is a good quick development framework like?
随机推荐
技术创造价值,手把手教你薅羊毛篇
Deep dive kotlin synergy (XIV): problems of shared state
最简单DIY基于STM32的远程控制电脑系统①(电容触摸+按键控制)
The problem of C language structure byte alignment
5 login failures, limiting login practice
圖片存儲--引用
Analysis of LinkedList source code
“互联网+”大赛命题火热对接中 | 一图读懂百度38道命题
Similarities and differences between SPI and IIC
NOI OJ 1.4 04:奇偶ASCII值判断 C语言
web技术分享| 【高德地图】实现自定义的轨迹回放
最简单DIY基于51单片机、PCA9685、IIC、云台的舵机集群控制程序
最简单DIY基于蓝牙、51单片机和舵机的钢铁爱国者机关枪控制器
【黄金分割点】与【斐波那契数列】
Win10 微软输入法(微软拼音) 不显示 选字栏(无法选字) 解决方法
详解判断大小端的方法
韦东山设备信息查询例程学习
Noi OJ 1.4 04: odd even ASCII value judgment C language
NOI OJ 1.2 整型与布尔型的转换 C语言
Unity technical manual - limit velocity over lifetime sub module and inherit velocity sub module