输入“/”快速插入内容

分布式锁

为什么需要分布式锁
在多线程环境中,若多个线程同时访问共享资源(如商品库存、外卖订单等),会发生数据竞争,可能会导致出现脏数据或者系统问题,影响程序的正常运行
举个例子:假设现在有100个用户参与某个限时秒杀活动,每位用户限购1件商品,且商品数量只有3个。若不对共享资源进行互斥访问,就可能出现以下情况
线程1、2、3等多个线程同时进入抢购方法,每个线程对应一个用户
线程1查询用户已经抢购的数量,发现当前用户尚未抢购且商品库存还有 1 个,因此认为可以继续执行抢购流程
线程 2 也执行查询用户已经抢购的数量,发现当前用户尚未抢购且商品库存还有 1 个,因此认为可以继续执行抢购流程
线程 1 继续执行,将库存数量减少 1 个,然后返回成功
线程 2 继续执行,将库存数量减少 1 个,然后返回成功
此时就发生了超卖问题,导致商品被多卖了一份
为保证共享资源被安全地访问,我们需要使用互斥操作对共享资源进行保护,即同一时刻只允许一个线程访问共享资源,其他线程需要等待当前线程释放后才能访问,这样可避免数据竞争和脏数据问题,保证程序的正确性和稳定性
如何才能实现共享资源的互斥访问呢?
悲观锁是一个比较通用的解决方案
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),故每次在获取资源操作时都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。共享资源每次只给一个线程使用,其他线程阻塞,用户再把资源转让给其他线程
对于单机多线程来说,Java中我们通常使用ReentrantLocksynchronized等JDK自带的本地锁来控制一个JVM进程内的多个线程对本地共享资源的访问
分布式系统下,不同服务/客户端通常运行在独立的JVM进程上,若多个JVM进程共享同一份资源,使用本地锁无法实现资源的互斥访问,再此背景下分布式诞生了
举个例子:系统的订单服务共部署了3份,都对外提供服务,用户下订单之前需要检查库存,为了防止超卖,这里需要加锁以实现对检查库存操作的同步访问,由于订单服务位于不同进程中,本地锁在这种情况下无法正常工作,我们需要用到分布式锁,这样可保证多个线程在不同一个JVM进程中也能获取同一把锁,进而实现共享资源的互斥访问
分布式锁应该具备的条件
一个最基本的分布式锁需要满足:
互斥:任意一个时刻,锁只能被一个线程持有
高可用:锁服务是高可用的,当一个锁服务出现问题,能够自动切换到另一个锁服务。即使客户端的释放锁的代码逻辑出现问题,锁最终一定还是会被释放,不会影响其他线程对共享资源的访问,这种一般是通过超时机制实现
可重入:一个节点获取了锁之后,还可再次获取锁
一个好的分布式锁还需要满足下面这些条件