Redis实现分布式锁

在JVM同一个进程内的线程

单进程的并发场景,我们可以使用语言和类库提供的锁如Sychronized和ReentranLock,对于部分不是的场景,我们需要使用分布式锁。

分布式锁的实现方式

  • Memcached分布式锁:利用Memcached的add命令,此命令是原子性操作,只有在key不存在的情况下,才能add成功。
  • Redis分布式锁:类似于Memcached,redis的setnx命令也是原子性操作。
  • Zookeeper分布式锁;

如何使用Redis实现分布式锁

加锁 setnx(key,1)

当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁,当一个线程执行setnx返回0,说明key已经存在,该线程抢锁失败。

解锁 del(key)

当得到锁的线程执行完任务,需要释放锁,以便其他线程进入,释放锁的简单方式执行del指令。

锁超时 (key,30)

如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程再也别想进来。

if(setnx(key,1)==1){
	expire(key,30);
	try{
		do something ...
	}finally{
		del(key)
	}
}

setnx和expire之间不是原子性操作,如果执行完setnx,节点宕机了,那么锁就永远得不到释放。在redis2.6.12提供了set指令增加了可选参数。set(key,1,30,NX)

del导致误删

如果某些原因导致线程A执行很慢,过了30秒都还没有执行完,这时候锁过期自动释放,线程B得到了锁,随后线程A执行完任务,线程A接着执行del释放锁,但这时候B还没有执行完,线程A释放的是线程B加的锁。

解决方法:在del释放锁之前加一个判断,验证当前的锁是不是自己加的,可以在加锁的时候那当前线程id当做value,并在删除之前验证key对应的value是不是自己的线程ID。

//加锁
String threadId=Thread.currentThread().getId;
set(key,threadId,30,NX);

//解锁
if(threadId.equals(redisClient.get(key))){
	del(key);
}

出现并发的可能性

虽然我们避免了线程A误删掉key的情况,但是同一时间有A,B两个线程在访问代码块,仍然使不完美的。我们可以获得锁的线程开启一个守护线程,用来给快要过期的锁续航。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值