Redisson - 分布式锁和同步器

news2025/3/31 6:34:57

文章目录

  • 锁(Lock)
  • 公平锁(Fair Lock)
  • 联锁(MultiLock)
  • 红锁(RedLock) 【已废弃】
  • 读写锁(ReadWriteLock)
  • 信号量(Semaphore)
  • 可过期许可信号量(PermitExpirableSemaphore)
  • 倒计时门闩(CountDownLatch)
  • 自旋锁(Spin Lock)
  • 栅栏锁(Fenced Lock)
  • 官方文档

在这里插入图片描述


锁(Lock)

基于Redis或Valkey的分布式可重入锁对象(Java实现),实现了Lock接口。通过发布/订阅(pub/sub)通道通知所有Redisson实例中等待获取锁的其他线程。

如果获取锁的Redisson实例崩溃,该锁可能会永远处于已获取状态。为避免此问题,Redisson维护了一个锁看门狗,当持有锁的Redisson实例存活时,它会延长锁的过期时间。默认锁看门狗超时时间为30秒,可通过Config.lockWatchdogTimeout配置修改。

在获取锁时可通过leaseTime参数指定锁的持有时间。超过指定时间后,锁将自动释放。

RLock对象的行为遵循Java锁规范,即只有锁的持有线程可以解锁,否则会抛出IllegalMonitorStateException。其他情况可考虑使用RSemaphore对象。

代码示例:

RLock lock = redisson.getLock("myLock");

// 传统加锁方式
lock.lock();

// 加锁并10秒后自动解锁
lock.lock(10, TimeUnit.SECONDS);

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

异步接口使用示例:

RLock lock = redisson.getLock("myLock");

long threadId = Thread.currentThread().getId();
RFuture<Void> lockFuture = lock.lockAsync(threadId);

// 加锁并10秒后自动解锁
RFuture<Void> lockFuture = lock.lockAsync(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
RFuture<Boolean> lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId);

lockFuture.whenComplete((res, exception) -> {
    // ...
    lock.unlockAsync(threadId);
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();
RLockReactive lock = redisson.getLock("myLock");

long threadId = Thread.currentThread().getId();
Mono<Void> lockMono = lock.lock(threadId);

// 加锁并10秒后自动解锁
Mono<Void> lockMono = lock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Mono<Boolean> lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockMono.doOnSuccess(res -> {
   // ...
})
.doFinally(r -> lock.unlock(threadId).subscribe())
.subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();
RLockRx lock = redisson.getLock("myLock");

long threadId = Thread.currentThread().getId();
Completable lockRes = lock.lock(threadId);

// 加锁并10秒后自动解锁
Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Single<Boolean> lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockRes.doOnSuccess(res -> {
   // ...
})
.doFinally(() -> lock.unlock(threadId).subscribe())
.subscribe();

公平锁(Fair Lock)

基于Redis或Valkey的分布式可重入公平锁(Java实现),实现了Lock接口。公平锁保证线程按请求顺序获取锁。所有等待线程会被队列化,若某线程异常终止,Redisson会等待其恢复5秒(例如5个线程异常终止,总延迟为25秒)。

锁看门狗机制与普通锁相同,支持leaseTime参数。解锁需由持有线程执行,否则抛出IllegalMonitorStateException

代码示例:

RLock lock = redisson.getFairLock("myLock");

// 传统加锁方式
lock.lock();

// 加锁并10秒后自动解锁
lock.lock(10, TimeUnit.SECONDS);

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

异步接口使用示例:

RLock lock = redisson.getFairLock("myLock");

RFuture<Void> lockFuture = lock.lockAsync();

// 加锁并10秒后自动解锁
RFuture<Void> lockFuture = lock.lockAsync(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
RFuture<Boolean> lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS);

lockFuture.whenComplete((res, exception) -> {
    // ...
    lock.unlockAsync();
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();
RLockReactive lock = redisson.getFairLock("myLock");

long threadId = Thread.currentThread().getId();
Mono<Void> lockMono = lock.lock(threadId);

// 加锁并10秒后自动解锁
Mono<Void> lockMono = lock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Mono<Boolean> lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockMono.doOnSuccess(res -> {
   // ...
})
.doFinally(r -> lock.unlock(threadId).subscribe())
.subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();
RLockRx lock = redisson.getFairLock("myLock");

long threadId = Thread.currentThread().getId();
Completable lockRes = lock.lock(threadId);

// 加锁并10秒后自动解锁
Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Single<Boolean> lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockRes.doOnSuccess(res -> {
   // ...
})
.doFinally(() -> lock.unlock(threadId).subscribe())
.subscribe();

联锁(MultiLock)

基于Redis或Valkey的分布式联锁对象,允许多个RLock对象(可属于不同Redisson实例)组合为单个锁。若持有联锁的Redisson实例崩溃,联锁可能永久处于已获取状态。锁看门狗机制与普通锁相同,支持leaseTime参数。解锁需由持有线程执行。

代码示例:

RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");

RLock multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3);

// 传统加锁方式
multiLock.lock();

// 加锁并10秒后自动解锁
multiLock.lock(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
boolean res = multiLock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       multiLock.unlock();
   }
}

异步接口使用示例:

RLock lock1 = redisson1.getLock("lock1");
RLock lock2 = redisson2.getLock("lock2");
RLock lock3 = redisson3.getLock("lock3");

RLock multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3);

long threadId = Thread.currentThread().getId();
RFuture<Void> lockFuture = multiLock.lockAsync(threadId);

// 加锁并10秒后自动解锁
RFuture<Void> lockFuture = multiLock.lockAsync(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
RFuture<Boolean> lockFuture = multiLock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId);

lockFuture.whenComplete((res, exception) -> {
    // ...
    multiLock.unlockAsync(threadId);
});

响应式接口使用示例:

RedissonReactiveClient anyRedisson = redissonClient.reactive();

RLockReactive lock1 = redisson1.getLock("lock1");
RLockReactive lock2 = redisson2.getLock("lock2");
RLockReactive lock3 = redisson3.getLock("lock3");

RLockReactive multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3);

long threadId = Thread.currentThread().getId();
Mono<Void> lockMono = multiLock.lock(threadId);

// 加锁并10秒后自动解锁
Mono<Void> lockMono = multiLock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Mono<Boolean> lockMono = multiLock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockMono.doOnSuccess(res -> {
   // ...
})
.doFinally(r -> multiLock.unlock(threadId).subscribe())
.subscribe();

RxJava3接口使用示例:

RedissonRxClient anyRedisson = redissonClient.rxJava();

RLockRx lock1 = redisson1.getLock("lock1");
RLockRx lock2 = redisson2.getLock("lock2");
RLockRx lock3 = redisson3.getLock("lock3");

RLockRx multiLock = anyRedisson.getMultiLock(lock1, lock2, lock3);

long threadId = Thread.currentThread().getId();
Completable lockRes = multiLock.lock(threadId);

// 加锁并10秒后自动解锁
Completable lockRes = multiLock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Single<Boolean> lockRes = multiLock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockRes.doOnSuccess(res -> {
   // ...
})
.doFinally(() -> multiLock.unlock(threadId).subscribe())
.subscribe();

红锁(RedLock) 【已废弃】

此对象已弃用,请改用RLockRFencedLock

RedLock 的背景

  • 问题场景:传统单节点 Redis 锁(如 SETNX)在主从切换时可能失效(例如主节点崩溃,锁未同步到从节点,导致其他客户端重复获取锁)。
  • 解决方案:Redis 作者 Antirez 提出 RedLock 算法,要求客户端在多个独立的 Redis 节点上获取锁,通过多数派机制提高可靠性。

RedLock 实现原理

Redisson 的 RedissonRedLock 类实现了 RedLock 算法,核心步骤如下:

  1. 获取锁

    • N 个独立 Redis 节点 依次发起加锁请求(使用相同键和随机值)。
    • 加锁成功条件:
      • 成功获取锁的节点数 ≥ N/2 + 1(多数派)。
      • 总耗时 < 锁的失效时间(避免锁过期后仍被误用)。
  2. 释放锁

    • 向所有节点发起解锁请求(无论是否加锁成功),避免残留锁状态。

Code

// 1. 创建多个独立 Redis 节点的客户端
RedissonClient client1 = Redisson.create(new Config().useSingleServer().setAddress("redis://node1:6379"));
RedissonClient client2 = Redisson.create(new Config().useSingleServer().setAddress("redis://node2:6379"));
RedissonClient client3 = Redisson.create(new Config().useSingleServer().setAddress("redis://node3:6379"));

// 2. 从每个客户端获取 RLock 对象
RLock lock1 = client1.getLock("myLock");
RLock lock2 = client2.getLock("myLock");
RLock lock3 = client3.getLock("myLock");

// 3. 创建 RedLock
RLock redLock = new RedissonRedLock(lock1, lock2, lock3);

try {
    // 4. 尝试加锁(最多等待 10 秒,锁有效期 30 秒)
    boolean success = redLock.tryLock(10, 30, TimeUnit.SECONDS);
    if (success) {
        // 执行业务逻辑
    }
} finally {
    redLock.unlock(); // 5. 释放锁
}

使用注意事项

  • 节点独立性:所有 Redis 节点必须为独立主节点(无主从关系),避免数据同步问题。
  • 锁有效期(Lease Time):需合理设置,确保业务逻辑能在锁失效前完成。
  • 性能开销:跨节点通信增加延迟,适合对可靠性要求高的场景。
  • 网络分区风险:极端情况下仍可能锁失效,需结合业务幂等性设计。

缺陷

  • 无法应对NPC异常场景
    RedLock 的核心算法无法正确处理网络延迟(Network Delay)、进程暂停(Process Pause)和时钟漂移(Clock Drift)这三种异常情况。例如:当持有锁的客户端因进程暂停(如JVM的STW)导致锁超时释放时,其他客户端可能同时获取同一资源的锁,破坏互斥性。若Redis节点时钟不同步,锁可能提前失效,导致多个客户端同时持有锁。

  • ​学术界与社区的批评
    Redis作者Antirez提出的RedLock算法曾遭到分布式系统专家Martin Kleppmann的质疑。Martin指出,RedLock依赖的“多数派加锁成功”机制无法保证严格的互斥性,尤其在节点故障恢复或主从切换时,可能产生锁状态不一致的问题。

RedLock的替代方案

  • ​ 联锁(MultiLock)​:允许一次性锁定多个资源,但需显式管理所有锁实例。
  • 单节点锁+WatchDog:通过定期续期避免锁超时,简化实现并兼顾性能

读写锁(ReadWriteLock)

基于Redis或Valkey的分布式可重入读写锁(Java实现),实现了ReadWriteLock接口。读锁和写锁均实现RLock接口,允许多个读锁或单个写锁。

锁看门狗机制与普通锁相同,支持leaseTime参数。解锁需由持有线程执行,否则抛出IllegalMonitorStateException

并发规则矩阵

请求类型已持读锁已持写锁
请求读锁✅允许❌拒绝
请求写锁❌拒绝❌拒绝

代码示例:

RReadWriteLock rwlock = redisson.getReadWriteLock("myLock");

RLock readLock = rwlock.readLock();
// 或
RLock writeLock = rwlock.writeLock();

// 传统加锁方式
readLock.lock();

// 加锁并10秒后自动解锁
writeLock.lock(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
boolean res = writeLock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       writeLock.unlock();
   }
}

异步接口使用示例:

RReadWriteLock rwlock = redisson.getReadWriteLock("myLock");

long threadId = Thread.currentThread().getId();
RLock readLock = rwlock.readLock();
// 或
RLock writeLock = rwlock.writeLock();

RFuture<Void> lockFuture = readLock.lockAsync(threadId);

// 加锁并10秒后自动解锁
RFuture<Void> lockFuture = writeLock.lockAsync(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
RFuture<Boolean> lockFuture = writeLock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId);

lockFuture.whenComplete((res, exception) -> {
    // ...
    writeLock.unlockAsync(threadId);
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();

RReadWriteLockReactive rwlock = redisson.getReadWriteLock("myLock");

RLockReactive readLock = rwlock.readLock();
// 或
RLockReactive writeLock = rwlock.writeLock();

long threadId = Thread.currentThread().getId();
Mono<Void> lockMono = writeLock.lock(threadId);

// 加锁并10秒后自动解锁
Mono<Void> lockMono = writeLock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Mono<Boolean> lockMono = writeLock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockMono.doOnSuccess(res -> {
   // ...
})
.doFinally(r -> writeLock.unlock(threadId).subscribe())
.subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();

RReadWriteLockRx rwlock = redisson.getReadWriteLock("myLock");

RLockRx readLock = rwlock.readLock();
// 或
RLockRx writeLock = rwlock.writeLock();

long threadId = Thread.currentThread().getId();
Completable lockRes = writeLock.lock(threadId);

// 加锁并10秒后自动解锁
Completable lockRes = writeLock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Single<Boolean> lockRes = writeLock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockRes.doOnSuccess(res -> {
   // ...
})
.doFinally(() -> writeLock.unlock(threadId).subscribe())
.subscribe();

信号量(Semaphore)

基于Redis或Valkey的分布式信号量(Java实现),与Java Semaphore类似。需通过trySetPermits(permits)初始化许可数量。

代码示例:

RSemaphore semaphore = redisson.getSemaphore("mySemaphore");

// 获取一个许可
semaphore.acquire();

// 获取10个许可
semaphore.acquire(10);

// 尝试获取许可
boolean res = semaphore.tryAcquire();

// 尝试获取许可(最多等待15秒)
boolean res = semaphore.tryAcquire(15, TimeUnit.SECONDS);

// 尝试获取10个许可
boolean res = semaphore.tryAcquire(10);

// 尝试获取10个许可(最多等待15秒)
boolean res = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       semaphore.release();
   }
}

异步接口使用示例:

RSemaphore semaphore = redisson.getSemaphore("mySemaphore");

// 获取一个许可
RFuture<Void> acquireFuture = semaphore.acquireAsync();

// 获取10个许可
RFuture<Void> acquireFuture = semaphore.acquireAsync(10);

// 尝试获取许可
RFuture<Boolean> acquireFuture = semaphore.tryAcquireAsync();

// 尝试获取许可(最多等待15秒)
RFuture<Boolean> acquireFuture = semaphore.tryAcquireAsync(15, TimeUnit.SECONDS);

// 尝试获取10个许可
RFuture<Boolean> acquireFuture = semaphore.tryAcquireAsync(10);

// 尝试获取10个许可(最多等待15秒)
RFuture<Boolean> acquireFuture = semaphore.tryAcquireAsync(10, 15, TimeUnit.SECONDS);

acquireFuture.whenComplete((res, exception) -> {
    // ...
    semaphore.releaseAsync();
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();

RSemaphoreReactive semaphore = redisson.getSemaphore("mySemaphore");

// 获取一个许可
Mono<Void> acquireMono = semaphore.acquire();

// 获取10个许可
Mono<Void> acquireMono = semaphore.acquire(10);

// 尝试获取许可
Mono<Boolean> acquireMono = semaphore.tryAcquire();

// 尝试获取许可(最多等待15秒)
Mono<Boolean> acquireMono = semaphore.tryAcquire(15, TimeUnit.SECONDS);

// 尝试获取10个许可
Mono<Boolean> acquireMono = semaphore.tryAcquire(10);

// 尝试获取10个许可(最多等待15秒)
Mono<Boolean> acquireMono = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);

acquireMono.doOnSuccess(res -> {
   // ...
})
.doFinally(r -> semaphore.release().subscribe())
.subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();

RSemaphoreRx semaphore = redisson.getSemaphore("mySemaphore");

// 获取一个许可
Completable acquireRx = semaphore.acquire();

// 获取10个许可
Completable acquireRx = semaphore.acquire(10);

// 尝试获取许可
Single<Boolean> acquireRx = semaphore.tryAcquire();

// 尝试获取许可(最多等待15秒)
Single<Boolean> acquireRx = semaphore.tryAcquire(15, TimeUnit.SECONDS);

// 尝试获取10个许可
Single<Boolean> acquireRx = semaphore.tryAcquire(10);

// 尝试获取10个许可(最多等待15秒)
Single<Boolean> acquireRx = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);

acquireRx.doOnSuccess(res -> {
   // ...
})
.doFinally(() -> semaphore.release().subscribe())
.subscribe();

可过期许可信号量(PermitExpirableSemaphore)

基于Redis或Valkey的分布式信号量,支持为每个许可设置租约时间。每个许可通过唯一ID标识,需使用该ID释放。需通过trySetPermits(permits)初始化许可数量,并通过addPermits(permits)动态调整。

代码示例:

RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");
semaphore.trySetPermits(23);

// 获取许可
String id = semaphore.acquire();

// 获取许可(租约10秒)
String id = semaphore.acquire(10, TimeUnit.SECONDS);

// 尝试获取许可
String id = semaphore.tryAcquire();

// 尝试获取许可(最多等待15秒)
String id = semaphore.tryAcquire(15, TimeUnit.SECONDS);

// 尝试获取许可(租约15秒,最多等待10秒)
String id = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);
if (id != null) {
   try {
     ...
   } finally {
       semaphore.release(id);
   }
}

异步接口使用示例:

RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");

RFuture<Boolean> setFuture = semaphore.trySetPermitsAsync(23);

// 获取许可
RFuture<String> acquireFuture = semaphore.acquireAsync();

// 获取许可(租约10秒)
RFuture<String> acquireFuture = semaphore.acquireAsync(10, TimeUnit.SECONDS);

// 尝试获取许可
RFuture<String> acquireFuture = semaphore.tryAcquireAsync();

// 尝试获取许可(最多等待15秒)
RFuture<String> acquireFuture = semaphore.tryAcquireAsync(15, TimeUnit.SECONDS);

// 尝试获取许可(租约15秒,最多等待10秒)
RFuture<String> acquireFuture = semaphore.tryAcquireAsync(10, 15, TimeUnit.SECONDS);
acquireFuture.whenComplete((id, exception) -> {
    // ...
    semaphore.releaseAsync(id);
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();

RPermitExpirableSemaphoreReactive semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");

Mono<Boolean> setMono = semaphore.trySetPermits(23);

// 获取许可
Mono<String> acquireMono = semaphore.acquire();

// 获取许可(租约10秒)
Mono<String> acquireMono = semaphore.acquire(10, TimeUnit.SECONDS);

// 尝试获取许可
Mono<String> acquireMono = semaphore.tryAcquire();

// 尝试获取许可(最多等待15秒)
Mono<String> acquireMono = semaphore.tryAcquire(15, TimeUnit.SECONDS);

// 尝试获取许可(租约15秒,最多等待10秒)
Mono<String> acquireMono = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);

acquireMono.flatMap(id -> {
   // ...
   return semaphore.release(id);
}).subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();

RPermitExpirableSemaphoreRx semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");

Single<Boolean> setRx = semaphore.trySetPermits(23);

// 获取许可
Single<String> acquireRx = semaphore.acquire();

// 获取许可(租约10秒)
Single<String> acquireRx = semaphore.acquire(10, TimeUnit.SECONDS);

// 尝试获取许可
Maybe<String> acquireRx = semaphore.tryAcquire();

// 尝试获取许可(最多等待15秒)
Maybe<String> acquireRx = semaphore.tryAcquire(15, TimeUnit.SECONDS);

// 尝试获取许可(租约15秒,最多等待10秒)
Maybe<String> acquireRx = semaphore.tryAcquire(10, 15, TimeUnit.SECONDS);

acquireRx.flatMap(id -> {
   // ...
   return semaphore.release(id);
}).subscribe();

倒计时门闩(CountDownLatch)

基于Redis或Valkey的分布式倒计时门闩(Java实现),需通过trySetCount(count)初始化计数器。

代码示例:

RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");

// 初始化计数器为1
latch.trySetCount(1);
// 等待计数器归零
latch.await();

// 其他线程或JVM中减少计数
RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");
latch.countDown();

异步接口使用示例:

RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");

RFuture<Boolean> setFuture = latch.trySetCountAsync(1);
// 等待计数器归零
RFuture<Void> awaitFuture = latch.awaitAsync();

// 其他线程或JVM中减少计数
RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");
RFuture<Void> countFuture = latch.countDownAsync();

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();
RCountDownLatchReactive latch = redisson.getCountDownLatch("myCountDownLatch");

Mono<Boolean> setMono = latch.trySetCount(1);
// 等待计数器归零
Mono<Void> awaitMono = latch.await();

// 其他线程或JVM中减少计数
RCountDownLatchReactive latch = redisson.getCountDownLatch("myCountDownLatch");
Mono<Void> countMono = latch.countDown();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();
RCountDownLatchRx latch = redisson.getCountDownLatch("myCountDownLatch");

Single<Boolean> setRx = latch.trySetCount(1);
// 等待计数器归零
Completable awaitRx = latch.await();

// 其他线程或JVM中减少计数
RCountDownLatchRx latch = redisson.getCountDownLatch("myCountDownLatch");
Completable countRx = latch.countDown();

自旋锁(Spin Lock)

基于Redis或Valkey的分布式可重入自旋锁(Java实现),采用指数退避策略避免因短时间内大量锁操作导致的网络吞吐量限制和Redis/Valkey CPU过载。锁看门狗机制与普通锁相同,支持leaseTime参数。解锁需由持有线程执行。

代码示例:

RLock lock = redisson.getSpinLock("myLock");

// 传统加锁方式
lock.lock();

// 加锁并10秒后自动解锁
lock.lock(10, TimeUnit.SECONDS);

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

异步接口使用示例:

RLock lock = redisson.getSpinLock("myLock");

long threadId = Thread.currentThread().getId();
RFuture<Void> lockFuture = lock.lockAsync(threadId);

// 加锁并10秒后自动解锁
RFuture<Void> lockFuture = lock.lockAsync(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
RFuture<Boolean> lockFuture = lock.tryLockAsync(100, 10, TimeUnit.SECONDS, threadId);

lockFuture.whenComplete((res, exception) -> {
    // ...
    lock.unlockAsync(threadId);
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();
RLockReactive lock = redisson.getSpinLock("myLock");

long threadId = Thread.currentThread().getId();
Mono<Void> lockMono = lock.lock(threadId);

// 加锁并10秒后自动解锁
Mono<Void> lockMono = lock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Mono<Boolean> lockMono = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockMono.doOnSuccess(res -> {
   // ...
})
.doFinally(r -> lock.unlock(threadId).subscribe())
.subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();
RLockRx lock = redisson.getSpinLock("myLock");

long threadId = Thread.currentThread().getId();
Completable lockRes = lock.lock(threadId);

// 加锁并10秒后自动解锁
Completable lockRes = lock.lock(10, TimeUnit.SECONDS, threadId);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁
Single<Boolean> lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS, threadId);

lockRes.doOnSuccess(res -> {
   // ...
})
.doFinally(() -> lock.unlock(threadId).subscribe())
.subscribe();

栅栏锁(Fenced Lock)

基于Redis或Valkey的分布式可重入栅栏锁(Java实现),通过**防护令牌(Fencing Token)**避免因客户端长时间停顿(如GC暂停)导致的锁失效问题。加锁方法返回令牌,服务需校验令牌是否递增。

锁看门狗机制与普通锁相同,支持leaseTime参数。解锁需由持有线程执行。

代码示例:

RFencedLock lock = redisson.getFencedLock("myLock");

// 加锁并获取令牌
Long token = lock.lockAndGetToken();

// 加锁并10秒后自动解锁,返回令牌
token = lock.lockAndGetToken(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁,返回令牌
Long token = lock.tryLockAndGetToken(100, 10, TimeUnit.SECONDS);
if (token != null) {
   try {
     // 校验令牌是否 >= 旧令牌
     ...
   } finally {
       lock.unlock();
   }
}

异步接口使用示例:

RFencedLock lock = redisson.getFencedLock("myLock");

RFuture<Long> lockFuture = lock.lockAndGetTokenAsync();

// 加锁并10秒后自动解锁,返回令牌
RFuture<Long> lockFuture = lock.lockAndGetTokenAsync(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁,返回令牌
RFuture<Long> lockFuture = lock.tryLockAndGetTokenAsync(100, 10, TimeUnit.SECONDS);

long threadId = Thread.currentThread().getId();
lockFuture.whenComplete((token, exception) -> {
    if (token != null) {
        try {
          // 校验令牌是否 >= 旧令牌
          ...
        } finally {
          lock.unlockAsync(threadId);
        }
    }
});

响应式接口使用示例:

RedissonReactiveClient redisson = redissonClient.reactive();
RFencedLockReactive lock = redisson.getFencedLock("myLock");

long threadId = Thread.currentThread().getId();
Mono<Long> lockMono = lock.lockAndGetToken();

// 加锁并10秒后自动解锁,返回令牌
Mono<Long> lockMono = lock.lockAndGetToken(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁,返回令牌
Mono<Long> lockMono = lock.tryLockAndGetToken(100, 10, TimeUnit.SECONDS);

lockMono.doOnSuccess(token -> {
    if (token != null) {
        try {
          // 校验令牌是否 >= 旧令牌
          ...
        } finally {
            lock.unlock(threadId).subscribe();
        }
    }
})
.subscribe();

RxJava3接口使用示例:

RedissonRxClient redisson = redissonClient.rxJava();
RFencedLockRx lock = redisson.getFencedLock("myLock");

Single<Long> lockRes = lock.lockAndGetToken();

// 加锁并10秒后自动解锁,返回令牌
Single<Long> lockRes = lock.lockAndGetToken(10, TimeUnit.SECONDS);

// 等待最多100秒尝试获取锁,并在获取后10秒自动解锁,返回令牌
Single<Long> lockRes = lock.tryLock(100, 10, TimeUnit.SECONDS);

long threadId = Thread.currentThread().getId();
lockRes.doOnSuccess(token -> {
    if (token != null) {
        try {
          // 校验令牌是否 >= 旧令牌
          ...
        } finally {
            lock.unlock(threadId).subscribe();
        }
    }
})
.subscribe();

官方文档

https://redisson.pro/docs/data-and-services/locks-and-synchronizers/

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2323225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Zustand 状态管理:从入门到实践

Zustand 状态管理&#xff1a;从入门到实践 Zustand 是一个轻量、快速且灵活的 React 状态管理库。它基于 Hooks API&#xff0c;提供了简洁的接口来创建和使用状态&#xff0c;同时易于扩展和优化。本文将通过一个 TODO 应用实例带你快速入门 Zustand&#xff0c;并探讨其核心…

PGP实现简单加密教程

模拟情景&#xff1a; 假设001和002两位同学的电脑上都安装了PGP&#xff0c;现在两人需要进行加密通讯。 一、创建密钥 1.新建密钥&#xff0c;输入名称和邮箱&#xff0c;输入8位口令&#xff0c;根据指示完成。 2.将其添加到主密钥&#xff0c;鼠标右击出现选项。 这里出…

7.8 窗体间传递数据

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 当项目中有多个窗体时&#xff08;在本节中为两个窗体&#xff1a;Form1和Form2&#xff09;&#xff0c;窗体间传递数据有以下几种方…

【redis】集群 数据分片算法:哈希求余、一致性哈希、哈希槽分区算法

文章目录 什么是集群数据分片算法哈希求余分片搬运 一致性哈希扩容 哈希槽分区算法扩容相关问题 什么是集群 广义的集群&#xff0c;只要你是多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称为是一个“集群” 前面的“主从结构”和“哨兵模式”可以称为是“广义的…

基于Springboot的网上订餐系统 【源码】+【PPT】+【开题报告】+【论文】

网上订餐系统是一个基于Java语言和Spring Boot框架开发的Web应用&#xff0c;旨在为用户和管理员提供一个便捷的订餐平台。该系统通过简化餐饮订购和管理流程&#xff0c;为用户提供快速、高效的在线订餐体验&#xff0c;同时也为管理员提供完善的后台管理功能&#xff0c;帮助…

【redis】集群 如何搭建集群详解

文章目录 集群搭建1. 创建目录和配置2. 编写 docker-compose.yml完整配置文件 3. 启动容器4. 构建集群超时 集群搭建 基于 docker 在我们云服务器上搭建出一个 redis 集群出来 当前节点&#xff0c;主要是因为我们只有一个云服务器&#xff0c;搞分布式系统&#xff0c;就比较…

飞牛NAS本地部署小雅Alist结合内网穿透实现跨地域远程在线访问观影

文章目录 前言1. VMware安装飞牛云&#xff08;fnOS&#xff09;1.1 打开VMware创建虚拟机1.3 初始化系统 2. 飞牛云搭建小雅Alist3. 公网远程访问小雅Alist3.1 安装Cpolar内网穿透3.2 创建远程连接公网地址 4. 固定Alist小雅公网地址 前言 嘿&#xff0c;小伙伴们&#xff0c…

Linux版本控制器Git【Ubuntu系统】

文章目录 **前言**一、版本控制器二、Git 简史三、安装 Git四、 在 Gitee/Github 创建项目五、三板斧1、git add 命令2、git commit 命令3、git push 命令 六、其他1、git pull 命令2、git log 命令3、git reflog 命令4、git stash 命令 七、.ignore 文件1、为什么使用 .gitign…

browser-use 库网页元素点击测试工具

目录 代码代码解释输出结果 代码 import asyncio import jsonfrom browser_use.browser.browser import Browser, BrowserConfig from browser_use.dom.views import DOMBaseNode, DOMElementNode, DOMTextNode from browser_use.utils import time_execution_syncclass Eleme…

解决GitLab无法拉取项目

1、验证 SSH 密钥是否已生成 ls ~/.ssh/ 如果看到类似 id_rsa 和 id_rsa.pub 的文件&#xff0c;则说明已存在 SSH 密钥。 避免麻烦&#xff0c;铲掉重来最方便。 如果没有&#xff0c;请生成新的 SSH 密钥&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexam…

FPGA学习篇——Verilog学习之寄存器的实现

1 寄存器理论 这里在常见的寄存器种加了一个复位信号sys_rst_n。&#xff08;_n后缀表示复位信号低电平有效&#xff0c;无这个后缀的则表示高电平有效&#xff09; 这里规定在时钟的上升沿有效&#xff0c;只有当时钟的上升沿来临时&#xff0c;输出out 才会改变&#xff0c;…

【VUE】ant design vue实现表格table上下拖拽排序

适合版本&#xff1a;ant design vue 1.7.8 实现效果&#xff1a; 代码&#xff1a; <template><div class"table-container"><a-table:columns"columns":dataSource"tableData":rowKey"record > record.id":row…

Vue实现动态数据透视表(交叉表)

需求:需要根据前端选择的横维度、竖维度、值去生成一个动态的表格&#xff0c;然后把交叉的值放入到对应的横维度和竖维度之下&#xff0c;其实就是excel里面的数据透视表功能&#xff0c;查询交叉语句为sql语句。 实现页面&#xff1a; 选择一下横维度、竖维度、值之后点击查…

推荐《人工智能算法》卷1、卷2和卷3 合集3本书(附pdf电子书下载)

今天&#xff0c;咱们就一同深入探讨人工智能算法的卷1、卷2和卷3&#xff0c;看看它们各自蕴含着怎样的奥秘&#xff0c;并且附上各自的pdf电子版免费下载地址。 《人工智能算法&#xff08;卷1&#xff09;&#xff1a;基础算法》 下载地址&#xff1a;https://www.panziye…

元宇宙浪潮下,数字孪生如何“乘风破浪”?

在当今科技飞速发展的时代&#xff0c;元宇宙的概念如同一颗璀璨的新星&#xff0c;吸引了全球的目光。元宇宙被描绘为一个平行于现实世界、又与现实世界相互影响且始终在线的虚拟空间&#xff0c;它整合了多种前沿技术&#xff0c;为人们带来沉浸式的交互体验。而数字孪生&…

数据分析 之 怎么看懂图 一

韦恩图怎么看 ①颜色:不同颜色代表不同的集合 ②)颜色重叠部分:表示相交集合共有的元素 ③颜色不重叠的部分:表示改集合独有的元素 ④数字:表示集合独有或共有的元素数量 ⑤百分比:表示该区域元素数占整体的比例 PCA图怎么看 ① 第一主成分坐标轴及主成分贡献率主成分贡献…

手写数据库MYDB(一):项目启动效果展示和环境配置问题说明

1.项目概况 这个项目实际上就是一个轮子项目&#xff0c;现在我看到的这个市面上面比较火的就是这个首先RPC&#xff0c;好多的机构都在搞这个&#xff0c;还有这个消息队列之类的&#xff0c;但是这个是基于MYSQL的&#xff0c;我们知道这个MYSQL在八股盛宴里面是重点考察对象…

深入理解椭圆曲线密码学(ECC)与区块链加密

椭圆曲线密码学&#xff08;ECC&#xff09;在现代加密技术中扮演着至关重要的角色&#xff0c;广泛应用于区块链、数字货币、数字签名等领域。由于其在提供高安全性和高效率上的优势&#xff0c;椭圆曲线密码学成为了数字加密的核心技术之一。本文将详细介绍椭圆曲线的基本原理…

Intellij IDEA2023 创建java web项目

Intellij IDEA2023 创建java web项目 零基础搭建web项目1、创建java项目2、创建web项目3、创建测试页面4、配置tomcat5、遇到的问题 零基础搭建web项目 小白一枚&#xff0c;零基础学习基于springMVC的web项目开发&#xff0c;记录开发过程以及中间遇到的问题。已经安装了Inte…

Scrapy结合Selenium实现滚动翻页数据采集

引言 在当今的互联网数据采集领域&#xff0c;许多网站采用动态加载技术&#xff08;如AJAX、无限滚动&#xff09;来优化用户体验。传统的基于Requests或Scrapy的爬虫难以直接获取动态渲染的数据&#xff0c;而Selenium可以模拟浏览器行为&#xff0c;实现滚动翻页和动态内容…