你的redis分布式锁bug之那些使用redis分布式锁你不知道的小细节

使用注意点:

1.0. synchronized在单机模式下可以保证多线程并发的安全性,但是若在集群模式下 synchronized保证当前java虚拟机的线程安全,如果集群在每个服务器上都有自己的tomcat和jvm 这个情况下 就不能保证线程安全。 由此引出redis分布式锁。

2.0 使用redis 的setnx命令(set if not exit) 如锁不存在返回true,如果存在则返回false

3.0 需要在使用redis锁解决并发的代码中(即原先使用synchronized的代码块)加上try{}finally{},这样保证在执行业务代码的过程中,如果业务代码出错finally中释放当前的锁。这样其他线程可以继续访问,不至于死锁。

4.0 还存在一种状况执行try中代码块的时候服务器突然宕机了,显然这时候finally中的代码块是执行不到的,这时候锁就不能得到释放。会造成死锁,别的线程也不能访问。解决方式:通过 设置锁的过期时间(通过redis的expire设置过期时间),如果中途服务器宕机,锁会在过期时间点自动释放。

5.0 若程序在使用redis的setnx之后程序就宕机了,这时候超时时间都不能设置,同样这是一个问题?解决方式如下6.

6.0 上边第四点过期时间设置的长度应该为多长,这需要经验来判断,但是存在一种状况,第一个线程调用使用redis锁的代码的时候如果用时8s,但是设置的超时时间是4秒。4秒后锁自动释放。 第二个线程进来但是它只需要三秒就执行完了,最后执行finally的代码,但是这时候第一个用时8s得线程现在还没有结束,但是第二个线程执行finally代码的时候显然是释放的是第一个线程的 锁,这样很多线程若都存在这种情况,那么redis的锁就会失效。 怎么解决:

    (1) 在利用redis设置setnx的时候setnx(key,value),这时候可以把value设置为当前线程的id,在finally代码块中释放前,拿当前线程的id和从redis中利用key取出的value值是否相等        若相等则释放。但是这种方式只能解决知己加的锁被其他线程释放的问题但是,存在还是4中那种情况,同一段代码被多个线程同时使用的问题。没有测地解决过期时间后,其他现在在
		前一个线程没有执行完的情况下,同时执行同一段代码(若这是电商订单的操作就会出现超卖的情况)
   (2)利用守护线程的思路:什么是守护线程?我么用上述例子来说明,当我们对redis的超时时间拿不定注意的时候,这时候我们可以在设置超时间后的后边,开单独的一个线程跑定时任务(Timmer)
          定时任务的时间可以根据redis设置超时的时间(注意小于超时时间,一般若超时时间为30秒定时任务设置为10秒)	,若超时时间之后检索当前线程是否还持有锁,若持有锁则延长锁的时间(在调用一次expire方法)。
   (3)针对(2)的开一个守护线程的方式,在java语言中有一个比较好的工具 利用redisson,直接调用lock方法,这时候的lock方法 同时设定了key的值和过期时间 ,巧妙了解决了5的问题,
        并且具有原子性。
复制代码

7.0 上边的6.0 基本上解决了redis分布锁大部分问题,但是此时还存在一个问题,如果你们公司redis是单机架构,上边的redis锁100%没有问题。 但是如果是redis是主从架构,或者是集群,(若是主从架构或者是集群主的redis都会向从的redis进行同步) 这时候主redis 挂了,但是还没有同步到从的redis上,或者其他redis上。这时候redis会主从切换,原来的从的redis变成了新的master。 但是由于没有得到数据同步,这时候其他线程访问的时候会重新加锁,这时候就会有出现上边的问题,多个线程同时执行同一段代码。但是主从同步没办法完全解决,由于redis的自身架构。 可以了解下redis主从复制。 8.0 代码会在后续中添加。

转载于:https://juejin.im/post/5c98e5116fb9a070c11f95bf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值