使用Redis的分布式Java锁

通过优锐课的java学习分享中,了解有关分布式锁定以及如何在项目中实现它的更多信息!

什么是分布式锁定?

在多线程程序中,不同的线程可能需要访问相同的资源。但是,允许所有线程同时访问资源可能导致争用情况,错误和其他意外行为。

为了确保没有两个线程可以同时访问同一资源,并确保以可预测的顺序对资源进行操作,程序员使用一种称为锁的机制。每个线程首先获取锁,然后对资源进行操作,最后将锁释放给其他线程。

在Java中,由于多种原因,锁定对象通常比使用同步块更灵活。首先,Lock API可以以不同的方法运行,而同步块完全包含在一个方法中。
另外,如果线程被阻止,则无法访问同步的块。使用Lock,该线程将仅在可用时获取锁。这大大减少了线程等待的时间。另外,当线程正在等待时,可以调用一种方法来中断线程,而当线程正在等待获取同步块时,这是不可能的。

分布式锁定意味着你不仅需要考虑多个线程或进程,还需要考虑在不同计算机上运行的不同客户端。这些单独的服务器必须进行协调,以确保它们中的任何一个在任何给定时间都在使用资源。

基于Redis的分布式Java锁定工具

Redisson框架是用于Java的基于Redis的内存数据网格,可为需要执行分布式锁定的程序员提供多个对象。 下面,我们将讨论每个选项及其之间的区别。

1.锁

RLock接口在Java中实现java.util.concurrent.locks.Lock接口。 这是一个可重入锁,这意味着线程可以多次锁定资源。 一个计数器变量跟踪锁定请求被执行了多少次。 一旦线程发出足够的解锁请求并且计数器达到0,资源便被释放。

以下简单代码示例演示了如何在Redisson中创建和初始化Lock:

RLock lock = redisson.getLock("anyLock");
// Most familiar locking method
lock.lock();
try {
  ...
} finally {
  lock.unlock();
}

如果获取此锁的Redisson实例崩溃,则该锁可能会在此获取状态下永久挂起。 为了避免这种情况,Redisson维护了一个锁“看门狗”,该“看门狗”会在持有该锁的Redisson实例仍处于活动状态时延长该锁的过期时间。 默认情况下,此锁定看门狗的超时为30秒。 可以通过Config.lockWatchdogTimeout设置更改此限制。

Redisson还允许你在获取租约时指定leaseTime参数。 在指定的时间间隔后,锁将自动释放:

// Acquire lock and release it automatically after 10 seconds
// if unlock method hasn‘t been invoked
lock.lock(10, TimeUnit.SECONDS);
// Wait for 100 seconds and automatically unlock it after 10 seconds
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

Redisson还为Lock对象提供了异步/响应/ rxjava2接口:

RLock lock = redisson.getLock("anyLock");
lock.lockAsync();
...
// Reactive Stream (Spring Project Reactor implementation)
RLockReactive lock = redissonReactive.getLock("anyLock");
Mono<Void> res = lock.lock();
...
// Reactive Stream (RxJava2 implementation)
RLockReactive lock = redissonRx.getLock("anyLock");
Flowable<Void> res = lock.lock();
...

由于RLock实现了Lock接口,因此只有拥有锁的线程才能解锁资源。 否则,任何尝试都会遇到IllegalMonitorStateException。

2. FairLock

与其表兄弟RLock一样,RFairLock也实现了java.util.concurrent.locks.Lock接口。 通过使用FairLock,你可以保证线程将按照请求资源的顺序来获取资源(即``先进先出‘‘队列)。 在为队列中的下一个线程解锁资源之前,Redisson会给已死掉五秒钟的线程重新启动。

与RLocks一样,创建和启动FairLock是一个简单的过程:

RLock lock = redisson.getFairLock("anyLock");
lock.lock();
try {
  ...
} catch {
  lock.unlock();
}
  1. ReadWriteLock

    Redisson的RReadWriteLock实现了java.util.concurrent.locks.ReadWriteLock接口。 在Java中,读/写锁实际上是两个锁的组合:一个只读锁可以同时由多个线程拥有,而写锁只能一次由一个线程拥有。

创建和初始化RReadWriteLock的方法如下:

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
rwlock.readLock().lock();
try {
  ...
} finally {
  rwlock.readLock().lock();
}
rwlock.writeLock().lock();
try {
  ...
} finally {
  rwlock.writeLock().lock();
}

4. RedLock

RedissonRedLock对象实现Redlock锁定算法,以将分布式锁与Redis一起使用:

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
lock.lock();
try {
  ...
} finally {
   lock.unlock();
}

在Redlock算法中,我们在单独的计算机或虚拟机上有许多独立的Redis主节点。 该算法尝试使用相同的键名和随机值依次获取这些实例中的每个实例的锁。 仅当客户端能够比锁有效的总时间更快地从大多数实例中获取锁时,才获取锁。

5.多重锁

RedissonMultiLock对象能够将多个单独的RLock实例组合在一起并作为单个实体进行管理:

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
lock.lock();
try {
  ...
} finally {
  lock.unlock();
}

正如我们在上面的示例中看到的那样,每个RLock对象可能属于不同的Redisson实例。 反过来,这可能会连接到其他Redis数据库。

结论

在本文中,我们探讨了Java开发人员可用于在Redis数据库之上的Redisson框架中执行分布式锁定的一些不同工具:Lock,FairLock,ReadWriteLock,RedLock和MultiLock。 有关Redisson中分布式计算的更多信息,请遵循GitHub上的项目Wiki。

原文地址:https://blog.51cto.com/14634606/2462682

时间: 2024-10-09 16:37:56

使用Redis的分布式Java锁的相关文章

【redis】基于redis实现分布式并发锁

基于redis实现分布式并发锁(注解实现) 说明 前提, 应用服务是分布式或多服务, 而这些"多"有共同的"redis"; GitHub: https://github.com/vergilyn/SpringBootDemo 代码结构: 一.分布式并发锁的几种可行方案 (具体实现思路参考: 分布式锁的实现.如何用消息系统避免分布式事务?) 1.基于数据库 可以用数据库的行锁for update, 或专门新建一张锁控制表来实现. 过于依赖数据库, 且健壮性也不是特别好

借助共享缓存redis实现分布式互斥锁

新开发的系统需要控制每个时刻回收缓存的GC线程有且只有一个在运行,如果有多个线程同时运行,会造成系统崩溃.如果只有一个JVM进程那么很好办,简单的借助synchronized关键字就行了.可是我的系统要部署在多台服务器,每台服务器上部署多个实例上.而synchronized仅仅在单进程里有用. 考虑借助共享数据源redis实现功能. redis提供一个方法,SETNX key value.将 key 的值设为 value ,当且仅当 key 不存在.若给定的 key 已经存在,则 SETNX 不

Redis之上的分布式Java队列

通过优锐课的java架构学习分享中,讨论了让我们使用Redisson Java框架讨论六种不同类型的基于Redis的分布式队列. 在Redis中使用队列 Redis是一个功能强大的工具,支持从字符串和列表到映射和流的许多不同类型的数据结构. 开发人员将Redis用于多种目的,包括用于数据库,缓存和消息代理. 像任何消息代理一样,Redis需要以正确的顺序发送消息. 可以根据消息的年龄或某些其他预定义的优先级等级发送消息. 为了存储这些未决消息,Redis开发人员需要队列数据结构. Redisso

Java实现基于Redis的分布式锁

我这里不实现JDK的java.util.concurrent.locks.Lock接口,而是自定义一个,因为JDK的有个newCondition()方法我这里暂时没实现.这个Lock提供了5个lock方法的变体,可以自行选择使用哪一个来获取锁,我的想法是 最好用带超时返回的那几个方法,因为不这样的话,假如redis挂了,线程永远都在那死循环了(关于这里,应该还可以进一步优化,如果redis挂了,Jedis的操作肯定会抛异常之类的,可以定义个机制让redis挂了的时候通知使用这个lock的用户,或

Redis实现分布式锁

http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性. 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock,我们相信这个算法会比一般的普通方法更加安全可靠.我们也希望社区能一起分析这个算法,

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决

基于Redis实现分布式锁以及任务队列

一.前言 双十一刚过不久,大家都知道在天猫.京东.苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这个手机,在高并发的情形下会对数据库服务器或者是文件服务器应用服务器造成巨大的压力,严重时说不定就宕机了,另一个问题是,秒杀的东西都是有量的,例如一款手机只有10台的量秒杀,那么,在高并发的情况下,成千上万条数据更新数据库(例如10台的量被人抢一台就会在数据集某些记录下 减1),那次这个

spring boot 利用redisson实现redis的分布式锁

原文:http://liaoke0123.iteye.com/blog/2375469 利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的. redis官方推荐的分布式锁实现为redisson http://ifeve.com/redis-lock/ 以下为spring boot实现分布式锁的步骤 项目pom中需要添加官方依赖 我是1.8JDK固为 Pom代码   <!-- redisson --> <dependency> <groupId>

如何优雅地用Redis实现分布式锁

https://mp.weixin.qq.com/s?__biz=MzAxNjM2MTk0Ng==&mid=2247484976&idx=2&sn=a0b6771f0b4e471c710f8cd51c243971&chksm=9bf4b685ac833f936f3722a795ae202a3be37a3fb57332393e2eec3bbf8b34c4705d5b14a964&mpshare=1&scene=1&srcid=0919f7t2duWiu