分布式锁

使用场景

抢券流程

在该场景下,线程1对数据库数据进行查询,之后线程2也对数据库的库存进行查询,查询结果都是1,但是线程1先执行了扣除库存的操作,库存这时为0,紧接着线程2也执行的删减库存的操作,这时出现的超买超卖的情况。

本地锁

多线程synchronized(/ˈsɪŋkrənaɪzd/)同步锁,通过加锁保证线程1的减库存操作执行完成后,再执行线程2,确保不会出现超买超卖的情况。

分布式锁

由于现在大部分公司的项目面对大部分的群体,为了保证服务能应对这种并发场景,服务采用集群式,同一个服务部署在多台Tomcat服务器上,此时出现的问题是集群中一个实例的锁并不能共享到另一个实例里面,故这种业务场景只能使用分布式锁。

setnx命令

Redis实现分布式锁主要利用Redis的setnx命令。setnx是set if not exists(如果不存在,则set)的简写

获取锁

1
2
#添加锁,NX是互斥,EX是设置超时时间
SET lock value NX EX 10

设置失效时间是因为如果在加锁过后服务超时或者服务宕机,获取的锁没有被释放从而导致死锁。超过一定时间自动释放锁的机制。

释放锁

1
DEL key

Redisson实现的分布式锁

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void redisLock() throws InterruptedException{
//获取锁(重入锁),执行锁的名称
Rlock lock = redissonClient.getLock("longLock");

//尝试获取锁,参数分别是:(获取锁的最大等待时间,)
//boolean isLock = lock.tryLock(10,30,TimeUnit.SECONDS);

//加锁、设置过期时间等操作都是基于lua脚本完成
boolean isLock = lock.tryLock(10,TimeUnit.SECONDS);
//判断是否获取成功
if(isLock){
try{
System.out.println("执行业务");
} finally{
//释放锁
lock.unLock();
}
}
}

redisson底层是setnx和lua脚本(保证原子性)

可重入锁

重入问题是指获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁

field是线程id,每次获取锁value+1,释放锁将value-1,直接value值为0 ,重入次数为0时,删除锁

同一线程的锁才能重入