
分布式锁,式锁顾名思义,现原就是分布在分布式环境下使用的锁 。众所周知,式锁在并发编程中,现原我们经常需要借助并发控制工具 ,分布如 mutex、式锁synchronized 等,现原来保障线程安全。分布但是式锁,这种线程安全仅作用在同一内存环境中。现原在实际业务中,分布为了保障服务的云计算式锁可靠性,我们通常会采用多节点进行部署 。现原在这种分布式情况下 ,各实例间的内存不共享,线程安全并不能保证并发安全,如下例,同一实例中线程A与线程B之间的并发安全并不能保证实例1与实例2之间的并发安全:

因此,当遇到分布式系统的并发安全问题时 ,我们就可能会需要引入分布式锁来解决。
用于实现分布式锁的组件通常都会具备以下的一些特性 :
互斥性 :提供分布式环境下的模板下载互斥原语来加锁/释放锁,当然是分布式锁最基本的特性。 自动释放:为了应对分布式系统中各实例因通信故障导致锁不能释放的问题,自动释放的特性通常也是很有必要的 。分区容错性:应用在分布式系统的组件 ,具备分区容错性也是一项重要的特性,否则就会成为整个系统的建站模板瓶颈 。目前开源社区中常见的分布式锁解决方案,大多是基于具备集群部署能力的 key-value 存储中间件来实现,最为常用的方案基本上是基于 Redis 、zookeeper 来实现 ,笔者将从上述分布式锁的特性出发 ,介绍一下这两类的分布式锁解决方案的优缺点。
Redis 由于其高性能 、使用及部署便利性 ,在很多场景下是实现分布式锁的首选。首先我们看下 Redis 是如何实现互斥性的 。在单机部署的模式下,Redis 由于其单线程处理命令的线程模型 ,天然的具备互斥能力;而在哨兵/集群模式下 ,写命令也是免费模板单独发送到某个单独节点上进行处理,可以保证互斥性;其核心的命令是 set [NX](set if ot exist):
复制SET lockKey lockValue NX1.成功设置 lockValue 的实例,就相当于抢锁成功 。但如果持有锁的实例宕机 ,因为 Redis 服务端并没有感知客户端状态的能力 ,因此会出现锁无法释放的问题:

这种情况下,就需要给 key 设置一个过期时间 expireTime:
复制SET lockKey lockValue EX expireTime NX1.左右滑动查看完整代码
如果持有锁的实例宕机无法释放锁,则锁会自动过期,这样可以就避免锁无法释放的亿华云问题。在一些简单的场景下 ,通过该方式实现的分布式锁已经可以满足需求。但这种方式存在一个明显问题 :如果业务的实际处理时间比锁过期时间长,锁就会被误释放 ,导致其他实例也可以加锁:

这种情况下 ,就需要通过其他机制来保证锁在业务处理结束后再释放 ,一个常用的方式就是通过后台线程的方式来实现锁的自动续期 。

Redssion 是开源社区中比较受欢迎的一个 Java 语言实现的 Redis 客户端 ,其对 Java 中 Lock 接口定义进行扩展 ,实现了 Redis 分布式锁 ,并通过 watchDog 机制(本质上即是后台线程运作)来对锁进行自动续期 。以下是一个简单的 Reddison 分布式锁的使用例子:
复制RLock rLock = RedissonClient.getLock("test-lock"); try { if (rLock.tryLock()) { // do something } } finally { rLock.unlock(); }1.2.3.4.5.6.7.8.左右滑动查看完整代码
Redssion 的默认实现 RedissonLock 为可重入互斥非公平锁,其 tryLock 方法会基于三个可选参数执行 :
waitTime(获取锁的最长等待时长):默认为-1 ,waitTime 参数决定在获取锁的过程中是否需要进行等待,如果 waitTime>0 ,则在获取锁的过程中线程会等待一定时间并持续尝试获取锁,否则获取锁失败会直接返回 。leaseTime(锁持有时长):默认为-1。当 leaseTime<=0 时,会开启 watchDog 机制进行自动续期,而 leaseTime>0 时则不会进行自动续期 ,到达 leaseTime 锁即过期释放unit(时间单位)