Redisson 分布式锁在 Java 中的使用场景

在上几章的内容中,我们讲了一些关于redis与redisson进行实现分布式的底层原理,所以这一节呢,我们来学习一下具体有哪些的使用场景。

还没有看过底层源码的同学可以看一下:

Redis 分布式锁的实现原理 - 掘金

RedissonLock加锁与解锁的实现原理 - 掘金

不设置超时时间与过期时间(直接使用)

1. 防止超卖

假设您有一个电商网站,正在销售限量版商品。为了防止超卖,可以使用 Redisson 分布式锁来锁定库存。

csharp 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("stock:" + productId);

try {
    // 获取锁
    lock.lock();

    // 检查库存
    int stock = inventoryService.getStock(productId);
    if (stock <= 0) {
        throw new BusinessException("商品已售罄");
    }

    // 扣减库存
    inventoryService.deductStock(productId, 1);

    // 处理订单
    orderService.createOrder(userId, productId);
} finally {
    // 释放锁
    lock.unlock();
}

2. 实现分布式任务调度

假设您有一个分布式任务调度系统,需要确保每个任务只能由一个工作进程执行。可以使用 Redisson 分布式锁来实现任务的互斥执行。

csharp 复制代码
解释RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("task:" + taskId);

try {
    // 获取锁
    lock.lock();

    // 执行任务
    taskService.executeTask(taskId);
} finally {
    // 释放锁
    lock.unlock();
}

3. 保护缓存一致性

假设您有一个缓存系统,用于缓存数据库中的数据。为了保护缓存的一致性,可以使用 Redisson 分布式锁来防止缓存更新期间出现并发读写。

ini 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("cache:" + key);

try {
    // 获取锁
    lock.lock();

    // 检查缓存
    Object cachedValue = cacheService.get(key);
    if (cachedValue != null) {
        return cachedValue;
    }

    // 从数据库中加载数据
    Object data = databaseService.loadData(key);

    // 更新缓存
    cacheService.put(key, data);

    return data;
} finally {
    // 释放锁
    lock.unlock();
}

这些只是一些 Redisson 分布式锁在 Java 中的使用场景示例。Redisson 分布式锁可以用于各种场景来保护共享资源并防止并发冲突,从而提高应用程序的性能和可靠性。

以下是一些有关 Redisson 分布式锁的额外提示:

  • 使用 Redisson 分布式锁时,请务必设置合理的过期时间,以避免死锁。
  • Redisson 分布式锁是可重入的,这意味着同一个线程可以多次获得同一把锁。但是,请注意不要在不必要的情况下持有锁太长时间,以免降低应用程序的性能。
  • Redisson 分布式锁是公平的,这意味着每个线程都有机会获得锁。但是,在某些情况下,可能需要使用非公平锁来优先考虑某些线程。

设置一定的超时时间

1. 使用 lock.lock(long timeout, TimeUnit unit) 方法

csharp 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("myLock");

try {
    // 设置超时时间 10 秒,并获取锁
    boolean acquired = lock.tryLock(10, TimeUnit.SECONDS);
    if (acquired) {
        // 执行业务逻辑
        // ...
    } else {
        // 未能获得锁,处理超时情况
        System.out.println("未能获得锁,请稍后再试");
    }
} finally {
    // 释放锁
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

2. 使用 lock.lock(AtomicReference leaseTime, TimeUnit unit) 方法

csharp 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("myLock");

AtomicReference<Long> leaseTime = new AtomicReference<>(10L);

try {
    // 设置超时时间 10 秒,并获取锁
    boolean acquired = lock.tryLock(leaseTime, TimeUnit.SECONDS);
    if (acquired) {
        // 执行业务逻辑
        // ...

        // 续期锁
        while (true) {
            long newLeaseTime = leaseTime.getAndSet(leaseTime.get() + leaseTime.get());
            if (lock.tryLock(newLeaseTime, TimeUnit.SECONDS)) {
                break;
            }
        }
    } else {
        // 未能获得锁,处理超时情况
        System.out.println("未能获得锁,请稍后再试");
    }
} finally {
    // 释放锁
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

3. 使用 lock.lock(RFunction<Long, Long> leaseTimeFunction, TimeUnit unit) 方法

csharp 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("myLock");

try {
    // 设置动态超时时间,并获取锁
    boolean acquired = lock.tryLock(r -> {
        // 根据实际情况计算超时时间
        long timeout = calculateTimeout();
        return timeout;
    }, TimeUnit.SECONDS);
    if (acquired) {
        // 执行业务逻辑
        // ...

        // 续期锁
        while (true) {
            long newLeaseTime = calculateTimeout();
            if (lock.tryLock(newLeaseTime, TimeUnit.SECONDS)) {
                break;
            }
        }
    } else {
        // 未能获得锁,处理超时情况
        System.out.println("未能获得锁,请稍后再试");
    }
} finally {
    // 释放锁
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

请注意,在实际使用中,您需要根据具体的业务需求来选择合适的设置超时时间的方法。

以下是一些有关 Redisson 分布式锁设置超时时间的额外提示:

  • 设置合理的超时时间可以有效防止死锁,但也不要设置过短的超时时间,以免造成锁的频繁释放和获取,降低应用程序的性能。
  • 在使用动态超时时间函数时,请确保该函数能够根据实际情况返回合理的超时时间。
  • Redisson 分布式锁的看门狗机制会定期检查锁的超时时间,并自动续期锁。但是,为了提高可靠性,建议您在业务逻辑中也进行手动续期操作。

设置最大超时时间和过期时间

1. 使用 lock.lock(long timeout, TimeUnit unit, long leaseTime, TimeUnit unit) 方法

csharp 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("myLock");

try {
    // 设置最大超时时间 10 秒和过期时间 15 秒,并获取锁
    boolean acquired = lock.tryLock(10, TimeUnit.SECONDS, 15, TimeUnit.SECONDS);
    if (acquired) {
        // 执行业务逻辑
        // ...

        // 续期锁
        while (true) {
            long timeRemaining = lock.remainingLeaseTime(TimeUnit.SECONDS);
            if (timeRemaining <= 5) {
                long newLeaseTime = 15L;
                if (lock.tryLock(newLeaseTime, TimeUnit.SECONDS)) {
                    break;
                }
            }
        }
    } else {
        // 未能获得锁,处理超时情况
        System.out.println("未能获得锁,请稍后再试");
    }
} finally {
    // 释放锁
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

2. 使用 lock.lock(RFunction<Long, Long> leaseTimeFunction, TimeUnit unit, RFunction<Long, Long> expireTimeFunction, TimeUnit unit) 方法

csharp 复制代码
RedissonClient redissonClient = RedissonClient.create();
RLock lock = redissonClient.getLock("myLock");

try {
    // 设置动态最大超时时间和过期时间,并获取锁
    boolean acquired = lock.tryLock(r -> {
        // 根据实际情况计算最大超时时间
        long timeout = calculateTimeout();
        return timeout;
    }, TimeUnit.SECONDS, r -> {
        // 根据实际情况计算过期时间
        long expireTime = calculateExpireTime();
        return expireTime;
    }, TimeUnit.SECONDS);
    if (acquired) {
        // 执行业务逻辑
        // ...

        // 续期锁
        while (true) {
            long timeRemaining = lock.remainingLeaseTime(TimeUnit.SECONDS);
            if (timeRemaining <= 5) {
                long newLeaseTime = calculateTimeout();
                long newExpireTime = calculateExpireTime();
                if (lock.tryLock(newLeaseTime, TimeUnit.SECONDS, newExpireTime, TimeUnit.SECONDS)) {
                    break;
                }
            }
        }
    } else {
        // 未能获得锁,处理超时情况
        System.out.println("未能获得锁,请稍后再试");
    }
} finally {
    // 释放锁
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

请注意,在实际使用中,您需要根据具体的业务需求来选择合适的设置最大超时时间和过期时间的方法。

以下是一些有关 Redisson 分布式锁设置最大超时时间和过期时间的额外提示:

  • 最大超时时间和过期时间是两个不同的概念。最大超时时间是客户端尝试获取锁的最长时间,而过期时间是锁的有效时间。
  • 设置最大超时时间可以防止客户端长时间等待锁,而设置过期时间可以防止由于意外情况导致锁无法释放而造成死锁。
  • 在使用动态最大超时时间或过期时间函数时,请确保该函数能够根据实际情况返回合理的数值。
相关推荐
煎饼小狗3 小时前
Redis五大基本类型——Zset有序集合命令详解(命令用法详解+思维导图详解)
数据库·redis·缓存
秋意钟4 小时前
缓存雪崩、缓存穿透【Redis】
redis
简 洁 冬冬5 小时前
046 购物车
redis·购物车
soulteary5 小时前
突破内存限制:Mac Mini M2 服务器化实践指南
运维·服务器·redis·macos·arm·pika
wkj0016 小时前
php操作redis
开发语言·redis·php
菠萝咕噜肉i7 小时前
超详细:Redis分布式锁
数据库·redis·分布式·缓存·分布式锁
登云时刻8 小时前
Kubernetes集群外连接redis集群和使用redis-shake工具迁移数据(二)
redis·容器·kubernetes
Dlwyz11 小时前
redis-击穿、穿透、雪崩
数据库·redis·缓存
工业甲酰苯胺13 小时前
Redis性能优化的18招
数据库·redis·性能优化
Oak Zhang16 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存