当前位置: 首页 > article >正文

Redisson - 分布式锁和同步器

文章目录

  • 锁(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/

在这里插入图片描述

相关文章:

Redisson - 分布式锁和同步器

文章目录 锁&#xff08;Lock&#xff09;公平锁&#xff08;Fair Lock&#xff09;联锁&#xff08;MultiLock&#xff09;红锁&#xff08;RedLock&#xff09; 【已废弃】读写锁&#xff08;ReadWriteLock&#xff09;信号量&#xff08;Semaphore&#xff09;可过期许可信号…...

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

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

[RITSEC CTF 2025] Crypto

这个忘打了&#xff0c;难度不小。 Alien Encryption 101 一个很小的RSA&#xff0c;略 Cuwves 2 Electric Boogaloo 已知p,在p^2下的两个椭圆曲线的j不变量&#xff0c;直接用函数 Mothership AES_CBC加密给出密文和IV&#xff0c;通过调整IV来修改明文 import base64 …...

算法250327题目

1114: 4006 AB问题 题目描述 给定两个整数A和B&#xff0c;其表示形式是&#xff1a;从个位开始&#xff0c;每三位数用逗号,隔开。 现在请计算AB的结果&#xff0c;并以正常形式输出。 输入 输入包含多组数据&#xff0c;每组数据占一行&#xff0c;由两个整数A和B组成&am…...

PGP实现简单加密教程

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

7.8 窗体间传递数据

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

一文了解 MCP Server:AI 工具与外部世界的桥梁

引言 随着大语言模型&#xff08;LLM&#xff09;的普及与 AI Agent 的爆发&#xff0c;Anthropic 于 2024 年底提出并开源的 Model Context Protocol&#xff08;MCP&#xff0c;模型上下文协议&#xff09;成为构建智能体系统的关键基石之一。本文将结合最新的实战经验&#…...

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

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

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

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

Redis常见面试问题汇总

Redis 面试笔记整理 一、Redis 基础知识1. Redis 概述Redis 是什么&#xff1f;主要特点有哪些&#xff1f;Redis 和 Memcached 的区别是什么&#xff1f;Redis 是单线程还是多线程&#xff1f;为什么单线程还能高效&#xff1f;Redis 6.0 之后的多线程模型是怎样的&#xff1f…...

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

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

NLP高频面试题(二十)——flash attention原理

FlashAttention是一种针对Transformer模型中自注意力机制的优化算法&#xff0c;旨在提高计算效率并降低内存占用&#xff0c;特别适用于处理长序列任务。 在Transformer架构中&#xff0c;自注意力机制的计算复杂度和内存需求随着序列长度的平方增长。这意味着当处理较长序列时…...

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

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

Episode, time step, batch, epoch

1. Episode&#xff08;回合&#xff09; 回合&#xff08;episode&#xff09;表示智能体从开始执行任务到完成任务&#xff08;例如成功到达目标或触发失败条件&#xff09;的全过程。 例如&#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…...

Vue 中使用 ECharts

在 Vue 中使用 ECharts 主要分为以下步骤&#xff0c;结合代码示例详细说明&#xff1a; 1. 安装 ECharts 通过 npm 或 yarn 安装 ECharts&#xff1a; npm install echarts --save # 或 yarn add echarts2. 基础使用&#xff08;完整引入&#xff09; 在 Vue 组件中使用 &…...

Spring AI + DeepSeek 构建大模型应用 Demo

Spring AI + DeepSeek 构建大模型应用 Demo 下面我将展示如何使用 Spring AI 框架结合 DeepSeek 的大模型能力构建一个简单的 AI 应用。 1. 环境准备 首先确保你已安装: JDK 17+Maven 3.6+Spring Boot 3.2+2. 创建 Spring Boot 项目 使用 Spring Initializr 创建项目,添加…...

解决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…...

POSIX 线程取消与资源清理完全指南

POSIX 线程取消与资源清理完全指南 引言&#xff1a;为什么需要线程取消机制&#xff1f; 在多线程编程中&#xff0c;优雅地终止线程并确保资源释放是开发者面临的重要挑战。直接终止线程可能导致内存泄漏、文件未关闭等问题。POSIX 线程库提供了一套完整的线程取消和清理机…...

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

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

Cursor异常问题全解析-无限使用

title: Cursor异常问题全解析无限使用 tags: cursor categories:aiai编程 mathjax: true description: Cursor异常问题全解析与解决方案大全 abbrlink: 64908bd0 date: 2025-03-19 14:48:32 &#x1f916; Assistant &#x1f6a8; Cursor异常问题全解析与解决方案大全 &…...

【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;为人们带来沉浸式的交互体验。而数字孪生&…...

WPF 附加属性

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;附加属性&#xff08;Attached Properties&#xff09;是一种特殊的依赖属性机制&#xff0c;它允许父元素为子元素提供额外的属性支持。这种特性特别适用于布局系统、输入处理和其他需要跨多个控件…...

数据分析 之 怎么看懂图 一

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

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

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

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

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