2018年3月10日 下午12:10
这个代码对应于tack/CloseOrderTask.java类,我这里就先不分析代码了!
- 我的通俗理解:小明和小王逛街累了抢一个椅子,当然是谁先抢上谁坐,说明椅子有主了。但是如果一个人坐在那里好多个小时,他始终不离开椅子,自己玩手机,另一个人就得一直等着他。等上一个小时,站着的哪个就火了,直接踹开那家伙,自己就坐了上去。
- 离开椅子的标志是:在redis中删除了CLOSE_ORDER_TASK_LOCK这个变量。
- 抢上椅子的标志为:在redis中设置了CLOSE_ORDER_TASK_LOCK这个变量
- 其实expire就可以起到防止死锁的作用,让50秒以后redis就会自动删除这个键。我理解这是一个双重保险。
版本一:
- 第一个版本:不使用redis,只是使用Spring Schedule使关单程序周期执行。但是由于我们是tomcat集群,这个关单程序会在两个tomcat同时执行,并且同时操作数据库。
- 一定会带来的问题:定时任务同时执行,对数据库的访问造成数据混乱
版本二:
- 第二个版本:使用redis做为分布式的锁,即使这个关单程序会在两个tomcat同时执行,但是会由于有这个锁的存在,使他们走不同的分支,只有其中一个分支可以执行关单程序业务流程,去操作数据库。如下图,这样就不会造成数据库的访问造成数据混乱。
- 极端条件下可能出现的问题:当我们setnx将“锁”放入redis之后,此时停止tomcat,那么原先放入的“锁”就会一直存在reids中,不会删除,以后由于这个“锁”的存在,永远不会执行真正的业务。最终造成死锁。
- 关键:
- 使用了setnx这个原子性的操作,这就保证了即使同时执行,也会有一个先来后到,一个走左分支,一个走右分支

- 使用了setnx这个原子性的操作,这就保证了即使同时执行,也会有一个先来后到,一个走左分支,一个走右分支
版本三:
- 第三个版本:假设,当我们setnx将“锁”放入redis之后,此时停止tomcat。那么此时两个tomcat中的同时执行的程序,永远都会走到“获取锁失败”的分支。那么我们就需要在执行了“获取锁失败”的分支中解决这个问题,在redis中这个“死锁”条件存在的情况下,也可以执行相关业务
- 关键:
- 在setnx时,我们的的“锁”key-value值要设置这个锁的超时时间,当然这个只是一个字符串的类型,是看的,而不是他自动会起作用的。
- 第一种情况:
- 还没有超过了这个“死锁”的超时时间
- 我们规定此时已然不能执行业务,必须等待这个“死锁”的时间到了
- 第二种情况:
- 已经超过了这个“死锁”的超时时间,我们此时就可以进行业务操作了,但是此时同样还是两个tomcat都要请求执行,那给谁呢?这不是回到了一开始的情况了吗
- 关键:
- 在第二个版本中我们使用原子操作setnx,来确定到底是哪个tomcat执行业务,在这里我们使用另一原子操作getset来区分。
- 总结:
- 对于判断一来说,两个tomcat要true都true,要false都false
- 对于判断二来说,一个tomcat是true一个tomcat是false

总结:
- redis 两个原子性操作是实现的关键
- Redis的中setnx的值,就是”锁”
- 一个锁,有两个时间
- 一个是setnx时的字符串value
- 一个是redis本身可以对单个数据设置的超时时间
