分布式锁
1.基于redis实现分布式锁
注意:这里设置过期时间,是为了预防死锁。如果某个线程获取了锁,但还没等它执行完业务,释放锁。服务器就宕机了,那么就不会有人再去释放锁,出现了死锁问题。
简单业务代码:
java
public interface ILock {
boolean tryLock(long timeoutSec);
//删除锁
void unLock();
}
public class SimpleRedisLock implements ILock {
//name 业务名字 (要知道是我哪个业务获取了锁,不可能所有业务获取一把锁啊)
private String name;
private StringRedisTemplate stringRedisTemplate;
private static String KEY_PREFIX = "lock:";
public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
this.name = name;
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 尝试获取锁
*
* @param timeoutSec 锁的过期时间
* @return 是否成功获取锁
*/
@Override
public boolean tryLock(long timeoutSec) {
long threadId = Thread.currentThread().getId();
String threadName = Thread.currentThread().getName();
Boolean success = stringRedisTemplate.opsForValue()
.setIfAbsent(KEY_PREFIX + this.name, threadName + threadId, timeoutSec, TimeUnit.SECONDS);
//不直接返回success是因为它是Boolean类型(可能为null),返回值是boolean(不能为null),有一个自动拆箱。可能会空指针异常
return Boolean.TRUE.equals(success);
}
@Override
public void unLock() {
String threadId = ID_PREFIX + Thread.currentThread().getId();
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + this.name);
if (id.equals(threadId)) {//只有当我释放锁的线程和我当时获取锁的线程是同一个时,才能释放锁!
stringRedisTemplate.delete(KEY_PREFIX + this.name);
}
}
}
2.基于Redisson实现分布式锁
业务代码:
lua脚本可以保证操作的原子性,类似事务,同时会执行成功/失败。
Redisson实现分布式锁的优点:
1.添加了Watch dog 可以给锁续期。
2.没抢到锁的线程会进行尝试等待。在高并发情况下增加分布式锁的使用性能。
3.所有的redis命令是基于lua脚本完成的。
Redisson实现分布式锁的可重入性
大概流程:底层通过hash结构存储,hash中的key存储的是获取锁的线程id,value是重入的次数。
在一个线程中,每次获取锁,重入次数+1,释放锁,重入次数-1。
对于上述代码:执行add1方法时,第一次获取锁,重入次数为1。执行add2方法中的获取锁,重入次数为2。add2释放锁,重入次数 - 1,为1。add1方法最后释放锁重入次数为0。删除锁信息!
小结: