分布式锁

在单机的并发编程中,我们经常遇到多线程环境下的线程安全问题,此时可以通过JDK提供的同步块synchronized或可重入锁ReentrantLock保证一段逻辑同时只有一个线程来执行。由此还引出了防止死锁、可重入锁、公平锁、读写锁等概念。在分布式环境下,情况就变得更加复杂了,一段需要控制线程安全的代码可能位于同一个服务的多个实例上,每个实例也可能多线程执行,此时JDK提供的锁就不能满足我们的需求了,我们需要分布式锁。

Redisson库基于Redis封装了分布式锁的实现,使用非常简单,这里我们简单介绍一下。

Redis分布式锁的局限性

在具体使用基于Redis的分布式锁前,我们必须要知道Redis的分布式锁具有的局限性。

实际生产环境中,Redis作为缓存系统一般都是以多节点的主从集群方式搭建,最常见的就是3主3从的6节点集群。我们知道分布式系统的CAP理论,(C)一致性、(A)可用性、(P)分区容错性不可兼得,Redis主从集群在这种情况下是倾向于AP的,因此在极端情况下,主节点宕机发生主从切换,而锁Key未能成功同步,此时就可能出现锁丢失的问题!

因此,我们应该避免使用基于Redis主从集群的分布式锁来控制必须具备强一致性的逻辑,此类可以选择单机Redis(具有单点故障风险),RedLock,或者使用ZooKeeper等其它CP分布式中间件来实现。

可重入锁 RLock

Redisson封装了分布式可重入锁RLock,例子代码如下。

// 获取锁
RLock lock = client.getLock("lock");
// 锁定
lock.lock();
// 判断当前线程是否持有锁
boolean isLock = lock.isHeldByCurrentThread();
// 主动解锁
lock.unlock();

注意RLock内部使用了看门狗机制,锁本身具有超时时间,而锁定过程中则会自动续期,因此能够避免节点宕机造成死锁的情况,这部分Redisson已经帮我们实现好了。

除了自动续期锁,我们也可以手动指定一个锁超时时间,此时Redisson不会进行自动续期。

// 锁定5秒
lock.lock(5, TimeUnit.SECONDS);

Redisson还实现了分布式可重入公平锁,获取公平锁的例子入下:

// 获取公平锁
RLock lock = client.getFairLock("fairLock");

公平锁能够严格按照加锁顺序分配锁。

红锁 RRedLock

RedLock是Redis作者提出的一种分布式锁,中文直译叫“红锁”,比较奇葩。

https://redis.com/redis-best-practices/communication-patterns/redlock/

RedLock需要单独搭建一组Redis集群,它需要具备单数个Master节点,且Master节点间没有任何数据同步机制(这和我们通常使用的6节点主从集群不同)。此时我们使用Redisson获取单数个RLock,并将其联合为1个RRedLock处理。

package com.gacfox.demo;

import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.*;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;

public class Main {
    public static void main(String[] args) {
        // Redis配置
        Config config1 = new Config();
        SingleServerConfig singleServerConfig1 = config1.useSingleServer();
        singleServerConfig1.setAddress("redis://127.0.0.1:6379");

        Config config2 = new Config();
        SingleServerConfig singleServerConfig2 = config2.useSingleServer();
        singleServerConfig2.setAddress("redis://127.0.0.1:6380");

        Config config3 = new Config();
        SingleServerConfig singleServerConfig3 = config3.useSingleServer();
        singleServerConfig3.setAddress("redis://127.0.0.1:6381");

        // 获取Redis客户端
        RedissonClient client1 = Redisson.create(config1);
        RedissonClient client2 = Redisson.create(config2);
        RedissonClient client3 = Redisson.create(config3);

        // 获取RRedLock
        RLock lock1 = client1.getLock("lock");
        RLock lock2 = client1.getLock("lock");
        RLock lock3 = client1.getLock("lock");
        RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

        // 获取锁
        redLock.lock();
        // 主动解锁
        redLock.unlock();

        // 关闭连接
        client1.shutdown();
        client2.shutdown();
        client3.shutdown();
    }
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap