在集群模式下,有多个服务器,当同一用户发起多次请求时,有几率分配到不同的服务器上,不同的服务器是不同的锁监视器,导致同一用户的多次请求都可以获取锁
分布式锁
本质是多个线程只有一个锁监视器,分布式系统或者集群模式下多进程可见并且互斥
基于Redis的分布式锁
获取锁,确保只有一个线程能够获取锁
释放锁,如果手动释放,假如线程获取锁后,服务器宕机,其他线程等待获取锁,获取锁的线程等请求释放锁,此时会进入死锁状态。所以要加入超时时间
如果线程刚刚获取锁,还未执行加入超时时间操作,服务器就宕机,此刻还是会进入死锁状态,所以必须保证获取锁和设置超时时间这两个操作是不可分割的,即原子性
版本一:
获取锁使用set key value nx ex time语句,保证了原子性,锁的是线程id,释放锁使用del语句
版本一存在的问题:
存在误删问题,假如线程一的业务阻塞,超过了超时时间,然后锁自动释放,此时线程二获取到锁,开始执行,假如在线程二执行过程中,线程一的业务完成,此时按照线程一的流程会直接执行del释放锁,线程二还在执行,会有安全问题
解决方法:
del前判断锁标识,原来是用纯粹的线程id来作为线程标识,现在可以用UUID+线程id来做线程标识,UUID用于区分不同的jvm,线程id用于区分不同的线程,在锁释放之前先进行判断,判断redis中的锁标识和现在想释放锁的线程是否为同一个
**但是现在会出现新的问题:**判断锁标识和释放锁是两个操作,当判断完锁标识后,此时因为jvm本身的原因,比如垃圾回收,可能会阻塞业务,如果阻塞时间足够长,超过了设置的锁超时时间,其他线程会获取锁,执行相应业务。当阻塞结束时,原先的线程因为判断过锁标识,现在会释放锁,导致线程安全问题
新问题解决方法:
使用lua脚本来保证判断锁标识和释放锁的原子性