目录
redis实现简易分布式锁
public String seckill2(){
String result = "seckill success";
String lock_key = "stock_num_lock_key";
String threadId = UUID.randomUUID().toString();
try {
//加锁
Boolean success = redisTemplate.opsForValue().setIfAbsent(lock_key, threadId,30,TimeUnit.SECONDS);
if(!success){
System.out.println("加锁失败");
return "get lock fail";
}
//加上一个自旋线程,防止代码块执行超时过期,导致key过期(自动解锁)
new Thread(){
@Override
public void run(){
String str = redisTemplate.opsForValue().get(lock_key);
while (threadId.equals(str)){
redisTemplate.expire(lock_key,10,TimeUnit.SECONDS);
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Integer stockNum = Integer.parseInt(redisTemplate.opsForValue().get("stock_num"));
// System.exit(0);
if(stockNum>0){
int newStockNum = stockNum-1;
redisTemplate.opsForValue().set("stock_num",newStockNum+"");
System.out.println("下单成功,新库存为::"+newStockNum);
result = "seckill success";
}else {
System.out.println("库存不足,下单失败");
result = "sekcill fail";
}
}finally {
//unlock
if(threadId.equals(redisTemplate.opsForValue().get(lock_key))){
redisTemplate.delete(lock_key);
}
}
return result;
}
redisson工作流程
加锁
加锁使用lua脚本保证复杂业务逻辑执行的原子性
lua 脚本优点:
- 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行
- 原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说,编写脚本的过程中无需担心会出现竞态条件
- 复用性,客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑
自旋线程(watch-dog)
注意
正常这个看门狗线程是不启动的,看门狗启动后对整体性能也会有一定影响,所以不建议开启看门狗。
自动对锁进行延期,防止锁的错乱,线程A的锁过期了,线程B的锁,被线程A解开了
redisson可重入锁
同一个线程可以重复拿到同一个资源的锁。重入锁非常有利于资源的高效利用。
1、Redis存储锁的数据类型是 Hash类型
2、Hash数据类型的key值包含了当前线程信息
锁的数据形式 <key,<key1,value>>
key :{ guid(唯一标识符):" 当前线程的id ” :锁重入次数 }
key: 锁的key值
value:
key1: guid : 当前线程的ID
value: 锁重入的次数