如何实现一个合格的分布式锁

1、概述

在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问,Java中我们一般可以使用 synchronized 语法和 ReentrantLock 去保证,这实际上是本地锁的方式。而在如今分布式架构的热潮下,如何保证不同节点的线程同步执行呢?

实际上,对于分布式场景,我们可以使用分布式锁,分布式锁是用于分布式环境下并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用。

图片

分布式锁的特点
  • 「互斥性:」 同一时刻只能有一个线程持有锁。

  • 「可重入性:」 同一节点上的同一个线程如果获取了锁之后能够再次获取锁。

  • 「锁超时:」 类似于J.U.C中的锁,支持锁超时,以防止死锁。

  • 「高性能和高可用:」 加锁和解锁需要高效,并且需要保证高可用性,防止分布式锁失效。

  • 「具备阻塞和非阻塞性:」 能够及时从阻塞状态中被唤醒。

2、Redis粗糙实现

Redis本身可以被多个客户端共享访问,是一个共享存储系统,适合用来保存分布式锁。由于Redis的读写性能高,可以应对高并发的锁操作场景。

Redis的SET命令有一个NX参数,可以实现「key不存在才插入」,因此可以用它来实现分布式锁:

  1. 如果key不存在,则表示插入成功,可以用来表示加锁成功;

  2. 如果key存在,则表示插入失败,可以用来表示加锁失败;

  3. 当需要解锁时,只需删除对应的key即可解锁成功;

  4. 为了避免死锁,需要设置合适的过期时间。

这样描述,我们可以得到一个十分粗糙的分布式锁实现。

// 尝试获得锁 
if (setnx(key, 1) == 1){  
// 获得锁成功,设置过期时间   
expire(key, 30) 
try {        
    //TODO 业务逻辑 

} finally {        
    // 解锁       
    del(key)  
  }
}

然而,上述实现方式存在一些问题,使其不能被称为合格的分布式锁:

  1. 「非原子性操作:」 多条命令的操作不是原子性的,可能会导致死锁的产生。

  2. 「锁误解除:」 存在锁误解除的可能性,即在持有锁的线程在内部出现阻塞时,锁的TTL到期导致自动释放,而其他线程误解除锁的情况。

  3. 「业务超时自动解锁导致并发问题:」 由于业务超时自动解锁,可能导致并发问题的发生。

  4. 「分布式锁不可重入:」 实现的分布式锁不支持重入。

3、解决遗留问题

3.1误删情况

在以下情况下可能会出现误删情况:

  • 持有锁的线程1在锁的内部出现了阻塞,导致其锁的TTL到期从而锁自动释放;

  • 此时线程2尝试获取锁,由于线程1已经释放了锁,线程2可以拿到;

  • 但是随后线程1解除阻塞,继续执行并开始释放锁&#x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图书API

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值