首页 > 其他分享 >常见分布式锁的基本实现

常见分布式锁的基本实现

时间:2022-12-27 18:41:00浏览次数:59  
标签:基本 10 加锁 lock 常见 Redisson TimeUnit 分布式

 

二、分布式锁应该具备哪些条件

在分析分布式锁的三种实现方式之前,先了解一下分布式锁应该具备哪些条件:

1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;

2、高可用的获取锁与释放锁;

3、高性能的获取锁与释放锁;

4、具备可重入特性;

5、具备锁失效机制,防止死锁;

6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

三、分布式锁的三种实现方式

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。
分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”
所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。


 

#redis
spring.redis.host=localhost
spring.redis.port=7001
spring.redis.password=
spring.redis.database=8
spring.redis.timeout=20000
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=3
spring.redis.lettuce.pool.max-wait=-1

 

8.1. 可重入锁(Reentrant Lock)

基于Redis的Redisson分布式可重入锁​​RLock​​​ Java对象实现了​​java.util.concurrent.locks.Lock​​​接口。同时还提供了​​异步(Async)​​​、​​反射式(Reactive)​​​和​​RxJava2标准​​的接口。

RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();

大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改​​Config.lockWatchdogTimeout​​来另行指定。

另外Redisson还通过加锁的方法提供了​​leaseTime​​的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
...
} finally {
lock.unlock();
}
}

Redisson同时还为分布式锁提供了异步执行的相关方法:

RLock lock = redisson.getLock("anyLock");
lock.lockAsync();
lock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS);

​RLock​​​对象完全符合Java的Lock规范。也就是说只有拥有锁的进程才能解锁,其他进程解锁则会抛出​​IllegalMonitorStateException​​​错误。但是如果遇到需要其他进程也能解锁的情况,请使用​​分布式信号量Semaphore​​ 对象.

8.2. 公平锁(Fair Lock)

基于Redis的Redisson分布式可重入公平锁也是实现了​​java.util.concurrent.locks.Lock​​​接口的一种​​RLock​​​对象。同时还提供了​​异步(Async)​​​、​​反射式(Reactive)​​​和​​RxJava2标准​​的接口。它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();

大家都知道,如果负责储存这个分布式锁的Redis节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改​​Config.lockWatchdogTimeout​​来另行指定。

另外Redisson还通过加锁的方法提供了​​leaseTime​​的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();

Redisson同时还为分布式可重入公平锁提供了异步执行的相关方法:

RLock fairLock = redisson.getFairLock("anyLock");
fairLock.lockAsync();
fairLock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = fairLock.tryLockAsync(100, 10, TimeUnit.SECONDS);

8.3. 联锁(MultiLock)

基于Redis的Redisson分布式联锁​​RedissonMultiLock​​​对象可以将多个​​RLock​​​对象关联为一个联锁,每个​​RLock​​对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改​​Config.lockWatchdogTimeout​​来另行指定。

另外Redisson还通过加锁的方法提供了​​leaseTime​​的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);

// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

8.4. 红锁(RedLock)

基于Redis的Redisson红锁​​RedissonRedLock​​​对象实现了​​Redlock​​​介绍的加锁算法。该对象也可以用来将多个​​RLock​​​对象关联为一个红锁,每个​​RLock​​对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改​​Config.lockWatchdogTimeout​​来另行指定。

另外Redisson还通过加锁的方法提供了​​leaseTime​​的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);

// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

8.5. 读写锁(ReadWriteLock)

基于Redis的Redisson分布式可重入读写锁​​RReadWriteLock​​​ Java对象实现了​​java.util.concurrent.locks.ReadWriteLock​​​接口。其中读锁和写锁都继承了​​RLock​​接口。

分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();

大家都知道,如果负责储存这个分布式锁的Redis节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改​​Config.lockWatchdogTimeout​​来另行指定。

另外Redisson还通过加锁的方法提供了​​leaseTime​​的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

8.6. 信号量(Semaphore)

基于Redis的Redisson的分布式信号量(​​Semaphore​​​)Java对象​​RSemaphore​​​采用了与​​java.util.concurrent.Semaphore​​​相似的接口和用法。同时还提供了​​异步(Async)​​​、​​反射式(Reactive)​​​和​​RxJava2标准​​的接口。

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();
//或
semaphore.acquireAsync();
semaphore.acquire(23);
semaphore.tryAcquire();
//或
semaphore.tryAcquireAsync();
semaphore.tryAcquire(23, TimeUnit.SECONDS);
//或
semaphore.tryAcquireAsync(23, TimeUnit.SECONDS);
semaphore.release(10);
semaphore.release();
//或
semaphore.releaseAsync();

8.7. 可过期性信号量(PermitExpirableSemaphore)

基于Redis的Redisson可过期性信号量(​​PermitExpirableSemaphore​​​)是在​​RSemaphore​​​对象的基础上,为每个信号增加了一个过期时间。每个信号可以通过独立的ID来辨识,释放时只能通过提交这个ID才能释放。它提供了​​异步(Async)​​​、​​反射式(Reactive)​​​和​​RxJava2标准​​的接口。

RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");
String permitId = semaphore.acquire();
// 获取一个信号,有效期只有2秒钟。
String permitId = semaphore.acquire(2, TimeUnit.SECONDS);
// ...
semaphore.release(permitId);

8.8. 闭锁(CountDownLatch)

基于Redisson的Redisson分布式闭锁(​​CountDownLatch​​​)Java对象​​RCountDownLatch​​​采用了与​​java.util.concurrent.CountDownLatch​​相似的接口和用法。

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();

// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

​https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8​

 


标签:基本,10,加锁,lock,常见,Redisson,TimeUnit,分布式
From: https://blog.51cto.com/u_15147537/5973141

相关文章

  • 常见的优先级选择模型 - 影响程度(痛点大于痒点)
    痛点需求一般情况大于痒点需求。只有因为难用而死掉的产品,没有因为难看而死掉的产品。痛点“痛点”是人们在完成某种行为、进行某种体验过程中的阻碍,是能够触发人强烈......
  • mysql的基本操作
    #mysql的基本操作###配置文件```mysql[mysqld]tmpdir=/mysql/tmp/basedir=/mysqldatadir=/mysql/datasymbolic-links=0character_set_server=utf8mb4sql_mode=NO_ENGIN......
  • 常见的优先级选择模型 - 四象限法则(重要紧急)
    四象限法则将需求按照紧急和重要两个维度划分为四类:重要且紧急,这类的事情可能是:明天有个重要的报告要提交、要考试了才开始看书、要上台表演了台词还没记熟...重要不紧......
  • Java基本数据类型
    目录1基本类型2基本类型的包装类2.1基本类型与包装类型区别与关联2.2装箱与拆箱2.3缓冲池3隐式类型转换4switch1基本类型Java里面只有八种原始类型,其列表如下:......
  • Selenium31-基本业务数据断言
    断言静态文本元素里的静态文本:就是指开始和结束标记之间的文本例如:文本定位到元素:一般是双标记的元素,目标文本位于两个<>之间示例:匿名用户然后获取元素对象的......
  • 10个中文成语,10种Python初学者常见错误
    哈喽兄弟们,我总结了Python中十种新手常见的错误,每一个都可以用成语来形容,看看各位遇到过多少次了!一、画蛇添足多余的分号Python语言与大多数编程语言不相同,它的......
  • FirewallD 基本知识(FirewallD入门教程)
    centos7中防火墙FirewallD是一个非常的强大的功能了,FirewallD提供了支持网络/防火墙区域(zone)定义网络链接以及接口安全等级的动态防火墙管理工具。它支持IPv4,IPv6......
  • GreenOpenPaint的实现(一)基本框架
    Win7下的MSPaint是Ribbon的典型运行。这种SDI程序对于图像处理来说是很适合的。利用春节时间,基于之前的积累,我实现GreenOpenPaint,主要就是模拟MSPaint的界面,实现出现处理的......
  • js中通过url传参的方式是很常见的,而传参的过程中获取参数也是很关键的一部分。本文主
    js中通过url传参的方式是很常见的,而传参的过程中获取参数也是很关键的一部分。本文主要分享js获取url参数的函数和调用方法。JS函数:1functiongetQuery(key){2......
  • YAML 文件基本语法格式(十四)
    一、YAML文件基本语法格式前面我们得Kubernetes集群已经搭建成功了,现在我们就可以在集群里面来跑我们的应用了。要在集群里面运行我们自己的应用,首先我们需要知道几个......