一,业务场景,秒杀系统
1,实现分布式锁
2,不能超卖
二,实现逻辑
1,synchronized,在抢购方法上加同步锁synchronized,弊端:只使用单机环境;很慢。只有所有逻辑执行完,下个人才能购买,把东西卖完耗时长
2,redis锁,在方法内部对商品加锁,解锁成功,继续执行购买操作,最后解锁。但是productId相同,不还是只用一个人能执行业务逻辑,其他用户只有等待吗?(暂不考虑为什么这样快)
三,加锁实现
3.1)如下,直接加锁,会导致两个问题
1,可能死锁,服务挂了,finally也没法释放
2,
3.2)针对上面的问题,直接加个过期时间不就行了,实际上是这样实现的,如下图
那中间这一坨代码实现什么功能?
1,设置过期时间,过期了,其他线程立即加锁执行购买
2,?
逻辑
1,线程1,加锁成功,返回true,下面代码不执行。这时候服务挂了,不还是死锁吗,没有过期时间?
假设起始时间为1000ms,即线程1进来时间
key:100
value:1000+100ms = 1100
线程1("100", "1100")
2,线程2,过了200ms,如果另外一个线程过来,当前时间1200
线程2("100", " 1300")1000+200+100 = 1300
currentValue:1100
当前系统时间:1000+200 = 1200 1100<1200,说明线程1过期了。
这时候业务可能没执行完(怎么办),且锁还没释放,在此获取时get/getAntSet时,发现锁过期了,才删除原来的锁,业务没执行完怎么办?锁已释放,是不是其他用户可以购买?是不是可以把值往大了设,确保业务逻辑执行完,finally释放锁(弊端:死锁了怎么办)(先不管)
3,如果线程1没过期,线程二返回false,加锁失败,不会执行购买逻辑,返回排队中...........
4,线程1过期了,线程2执行getAntSet,注意这里只有一个线程能执行成功(可能同时进来两个线程都执行到这里)
oldValue:1100
当前key value("100", "1300")
5,比较老值和当前值
老值:1100
currentValue:1100
相同,返回true,线程1失效时,线程2加锁成功(如果线程1逻辑没执行完,线程2也去执行业务逻辑,会不会导致超卖?)
6,然后呢,上面这些说明什么问题
实现了,如果前面线程过期,后面线程可以,立即加锁执行购买行为
然后呢,主要说明的是这种场景
线程1持有锁,并发情况下,线程2,线程3同时进入加锁方法,且value相同
线程1("100", "1100")
线程2,线程3("100", "1300")
如果当前时间1200,线程1过期,线程2设置了当前key value("100", "1300")
老值:1100
currentValue:1100,相同加锁成功
重要的是对于线程3来说,
currentValue:1100
oldValue:应该是线程2的值,1300
明显不相同,加锁失败,等待线程2执行。
巴拉巴拉差不多说完了,现在想想中间这坨代码干啥的?
1,过期了,其他人能够立刻购买成功
2,过期了,并发时,只要一个人能购买成功。如果不加这坨呢,直接加锁时加个过期时间,不过期时,其他线程过来返回false,有啥问题吗,又绕回去了。
想不通?
其他实现逻辑
1,判断是否被锁 isLock
2,是,不执行
3,否,加锁
4,释放锁
加锁方法返回Boolean,具体实现如下