redis--Redisson的八种锁机制
Redisson 的8种锁机制
Redisson 应用最多的场景就是其分布式锁RLock。
而RLock只是Redisson 线程同步方案之一。Redisson 提供了8种线程同步方案。可以针对不同场景选择不同方案。
为了避免锁到期但业务没执行完毕而引发并发问题,Redisson 内部提供了一个监控锁的看门狗 watch dog,其会在锁到期前不断延长锁的到期时间,直到锁被主动释放。即自动完成“锁续命”。
可重入锁
Redisson 的RLock是可重入锁。当一个线程获取到锁后,这个线程可以再次获取本对象上的锁,而其他线程不可以。
- JDK中的ReentrantLock是可重入锁,其是通过AQS(抽象队列同步器)实现的锁机制
- synchronized 也是可重入锁,其是通过监视器模式(本质是os的互斥锁)实现的锁机制
公平锁
Redisson 的可重入锁RLock默认是非公平锁,但也支持可重入公平锁FailLock.
多个线程同时申请锁时,这些线程会进入到一个FIFO队列,只有队首元素会获取到锁,其他元素等待。当锁被释放后,再将锁分配给当前的队首元素。
// 获取到公平锁,公平锁是按照请求锁的顺序来获取锁的,先请求锁的先获取到锁,后请求锁的后获取到锁。
RLock rlock = redisson.getFairLock(Redis_lock);
联锁
Redisson 联锁MultiLock .
当一个线程要处理多个共享资源时,可以使用联锁。
即一次性申请多个锁,同时锁定多个资源。
联锁可预防死锁,对共享资源的申请实现了原子性;要么都申请到,要么都申请不到;
其是OS底层原理中AND型信号量机制的典型应用。
RLock rlock1 = redisson1.getLock(Redis_lock+"_1");
RLock rlock2 = redisson2.getLock(Redis_lock+"_2");
RLock rlock3 = redisson3.getLock(Redis_lock+"_3");
// 2. 构建联锁(MultiLock)
RedissonMultiLock multiLock = new RedissonMultiLock(rlock1, rlock2, rlock3);
红锁
Redisson 红锁 RedLock。红锁由多个锁构成,当大部分这里的大部分锁申请成功,红锁才申请成功。
红锁一般用于解决Redis 主从集群锁丢失问题。
红锁与联锁的区别:
- 红锁实现对一个共享资源的同步访问控制
- 联锁实现对多个共享资源的同步访问控制
RLock rlock1 = redisson1.getLock(Redis_lock+"_1");
RLock rlock2 = redisson2.getLock(Redis_lock+"_2");
RLock rlock3 = redisson3.getLock(Redis_lock+"_3");
RedissonRedLock rlock = new RedissonRedLock(rlock1,rlock2,rlock3);
读写锁
Redisson 读写锁 RReadWriteLock. 通过RReadWriteLock实例可获取到读锁RedissonReadLock和RedissonWriteLock.
读锁和写锁分别实现了RLock的可重入锁。
一个共享资源,在没有写锁的情况下,允许加多个读锁。只要加了写锁,就不能加任何读锁与写锁。
读锁是共享锁,写锁为排他锁。
RReadWriteLock readWriteLock = redisson.getReadWriteLock(Redis_lock);
RLock rLock = readWriteLock.readLock();
RLock rLock1 = readWriteLock.writeLock();
信号量
Redisson 可以获取到信号量RSemaphore。RSemaphore的常用场景有两种:
- 无论谁加的锁,其他线程都可以解锁
- 当一个线程要申请多个资源时
RSemaphore 时信号量机制的典型应用。
// 假设Redis中存储key="redis_semaphore"的值为8,表示有8个信号量可用。
RSemaphore redisSemaphore = redisson.getSemaphore("redis_semaphore");
try {
// 申请一个信号 ,信号量-1
redisSemaphore.acquire();
// 一次申请3个信号,信号量-3
redisSemaphore.tryAcquire(3);
// 申请一个信号,等待时间为10秒,
redisSemaphore.tryAcquire(10, TimeUnit.SECONDS);
// 一次申请3个信号,等待时间为10秒,
redisSemaphore.tryAcquire(3,10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
// 释放一个信号,信号量+1
redisSemaphore.release();
// 一次释放3个信号,信号量+3
redisSemaphore.release(3);
}
可过期信号量
Redisson 可以获取到可过期信号量 PermitExpirableSemaphore .
它是在RSemaphore 基础上,为每个信号增加了过期时间,且每个信号通过独立的ID来辨识。释放时只能通过提交该ID才能释放。
一个线程每次只能申请一个信号量,每次也只能释放一个信号量。(与RSemaphore 不同的地方)
该信号量为互斥信号量时,等同于可重入锁。或者说,可重入锁就相当于信号量为1的可过期信号量。
可过期信号量与可重入锁的区别:
- 可重入锁:用户每次只能申请1个信号量,且只有一个用户可以申请成功
- 可过期信号量:用户没每次只能申请1个信号量,但可以有多个用户申请成功
RPermitExpirableSemaphore redisSemaphore1 = redisson.getPermitExpirableSemaphore("redis_semaphore");
String permitId="";
try {
// 申请一个信号,信号量-1,返回一个permitId,释放锁的时候需要用到这个permitId
permitId = redisSemaphore1.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
// 释放一个信号,信号量+1,释放锁的时候需要用到这个permitId
redisSemaphore1.release(permitId);
}
分布式闭锁
Redisson 可以获取到分布式闭锁 RCountDownLatch. 与JUC中的闭锁CountDownLatch原理相同,用法类似。常用于一个或多个线程的执行必须在其他任务执行完毕的场景。
例如:大规模分布式并行计算中,最终的合并计算必须基于很多的并向计算的计算结果。
闭锁中定义了一个计数器和一个阻塞队列。
阻塞队列中存放着待执行的线程。每当一个并行任务执行完毕,计数器就 -1 ,当计数器=0时,就会唤醒阻塞队列的所有线程。
通常使用Barrier 队列解决该问题,而Barrier 队列通常使用Zookeeper 实现。
// 获取闭锁对象(合并线程和条件线程都需要该代码)
RCountDownLatch countDownLatch = redisson.getCountDownLatch("count_down_latch");
// 合并线程代码
// 当闭锁的计数器为0或者闭锁不存在时,初始化闭锁的计数器为3
if(countDownLatch.getCount() == 0 || !countDownLatch.isExists()){
// 初始化闭锁的计数器为3,
countDownLatch.trySetCount(3);
}
try {
// 合并线程等待闭锁,等待条件线程执行完毕后,合并线程才会继续执行
countDownLatch.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// ==============================================================================
// 条件线程代码
// 条件线程执行完毕后,调用countDown方法,闭锁的计数器-1,当计数器为0时,合并线程才会继续执行
countDownLatch.countDown();
本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/20035820
浙公网安备 33010602011771号