Redisson 分布式锁和同步器

世界以痛吻我,我仍报之以歌。

可重入锁(Reentrant Lock)

基础使用示例:

1
2
RLock lock = redisson.getLock("lockKey");
lock.lock();

加锁以后10秒钟自动解锁示例:

1
2
RLock lock = redisson.getLock("lockKey");
lock.lock(10, TimeUnit.SECONDS);

尝试加锁,最多等待20秒示例:

1
2
3
4
5
6
7
8
boolean res = lock.tryLock(20, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

尝试加锁,最多等待20秒,上锁后10秒自动解锁示例:

1
2
3
4
5
6
7
8
boolean res = lock.tryLock(20, 10 TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

异步加锁示例:

lockAsync() 相比较 lock() 的区别是不阻塞主流程;lock() 必须加锁成功后才能顺序执行下面的代码;而 lockAsync() 不用等待加锁是否成功,继续执行下面的代码。

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

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

公平锁(Fair Lock)

它保证了当多个 Redisson 客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson 会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。

基础使用示例:

1
2
RLock fairLock = redisson.getFairLock("lockKey");
fairLock.lock();

加锁以后10秒钟自动解锁示例:

1
2
RLock fairLock = redisson.getFairLock("lockKey");
fairLock.lock(10, TimeUnit.SECONDS);

尝试加锁,最多等待20秒示例:

1
2
3
4
5
6
7
8
boolean res = fairLock.tryLock(20, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       fairLock.unlock();
   }
}

尝试加锁,最多等待20秒,上锁后10秒自动解锁示例:

1
2
3
4
5
6
7
8
boolean res = fairLock.tryLock(20, 10 TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       fairLock.unlock();
   }
}

异步加锁示例:

1
2
3
4
RLock fairLock = redisson.getFairLock("lockKey");
fairLock.lockAsync();
fairLock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = fairLock.tryLockAsync(100, 10, TimeUnit.SECONDS);

联锁(MultiLock)

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

基础使用示例:

1
2
3
4
5
6
7
8
9
10
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();
// todo
lock.unlock();

加锁超时示例:

1
2
3
4
5
6
7
8
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();

红锁(RedLock)

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

基础使用示例:

1
2
3
4
5
6
7
8
9
10
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();

加锁超时示例:

1
2
3
4
5
6
7
8
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();

读写锁(ReadWriteLock)

ReadWriteLock 管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。

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

基础使用示例:

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

加锁超时示例:

1
2
3
4
5
6
7
8
9
10
11
// 10秒钟以后自动解锁
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();

信号量(Semaphore)

用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。

可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。

基础使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
// 设置5个许可
semaphore.trySetPermits(5);
// 获取1个许可
semaphore.acquire();
// 获取5个许可
semaphore.acquire(5);
// 释放1个许可
semaphore.release();
// 释放5个许可
semaphore.release(5);
// 尝试获取1个许可
boolean b = semaphore.tryAcquire();
// 尝试获取5个许可
boolean b1 = semaphore.tryAcquire(5);
// 尝试获取5个许可,等待10秒
boolean b2 = semaphore.tryAcquire(10, TimeUnit.SECONDS);
// 异步获取1个许可
semaphore.acquireAsync();
// 异步尝试获取1个许可
semaphore.tryAcquireAsync();

可过期性信号量(PermitExpirableSemaphore)

在RSemaphore对象的基础上,为每个信号增加了一个过期时间。每个信号可以通过独立的ID来辨识,释放时只能通过提交这个ID才能释放。

基础使用示例:

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

闭锁(CountDownLatch)

CountDownLatch 有一个正数计数器,countDown() 方法对计数器做减操作,await() 方法等待计数器达到0。所有 await 的线程都会阻塞直到计数器为0或者等待线程中断或者超时。

基础使用示例:

1
2
3
4
5
6
7
8
9
10
RCountDownLatch latch = redissonClient.getCountDownLatch("anyCountDownLatch");
// 设置计数
latch.trySetCount(1);
// 阻塞直到计数器为0或者等待线程中断或者超时
latch.await();

// 在其他线程或其他JVM里
RCountDownLatch latch = redissonClient.getCountDownLatch("anyCountDownLatch");
// 计数减1
latch.countDown();