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

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;可过期许可信号…...

LabVIEW时间触发协议

介绍了基于LabVIEW开发的时间触发协议应用&#xff0c;通过实例解析了FlexRay总线的设计与优化。通过技术细节、系统构建和功能实现等方面&#xff0c;探讨了LabVIEW在现代工业通信系统中的应用效能&#xff0c;特别是在提高通信可靠性和实时性方面的贡献。 ​ 项目背景 在工…...

IDEA的使用

idea的介绍 IntelliJ IDEA 是由 JetBrains 公司开发的一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;主要用于Java语言的开发&#xff0c;但同时也支持其他多种编程语言如Kotlin、Groovy、Scala等。它被广泛认为是专业软件开发者的首选工具之一&#xff…...

unity一个图片的物体,会有透明的效果

如图 想要去掉这个透明效果 选择一个高层级的layer即可。...

IP报文格式

IPv4 头部结构&#xff08;共 20 字节&#xff0c;不含可选字段&#xff09; 1. 版本&#xff08;Version&#xff09; 长度&#xff1a;4 比特 作用&#xff1a;标识 IP 协议版本&#xff08;IPv4 值为 4&#xff0c;IPv6 值为 6&#xff09;。 示例&#xff1a;0100&#x…...

2025最新-智慧小区物业管理系统

目录 1. 项目概述 2. 技术栈 3. 功能模块 3.1 管理员端 3.1.1 核心业务处理模块 3.1.2 基础信息模块 3.1.3 数据统计分析模块 3.2 业主端 5. 系统架构 5.1 前端架构 5.2 后端架构 5.3 数据交互流程 6. 部署说明 6.1 环境要求 6.2 部署步骤 7. 使用说明 7.1 管…...

sql结尾加刷题

找了一下mysql对extractvalue()、updatexml()函数的官方介绍https://dev.mysql.com/doc/refman/5.7/en/xml-functions.html#function_extractvalue ExtractValue(xml_frag, xpath_expr) 知识点 解释一下这两个参数xml_frag&#xff0c;是xml标记片段&#xff0c;第二个参数…...

UE4学习笔记 FPS游戏制作26 UE中的UI

文章目录 几个概念创建一个UI蓝图添加UI获取UI的引用 切换设计器和UI蓝图将UI添加到游戏场景锚点轴点slotSizeToContent三种UI数据更新方式函数绑定属性绑定事件绑定 九宫格分割图片 几个概念 UMG&#xff1a;UE的UI编辑器 slate UI: UE的UI的编辑语言 创建一个UI蓝图 右键用…...

pnpm 依赖升级终极指南:从语义化版本控制到 Monorepo 全局更新的企业级实践

要使用 pnpm 更新所有依赖包&#xff0c;可以通过以下命令实现&#xff1a; 1. 更新所有依赖到符合语义化版本的范围 pnpm update该命令会根据 package.json 中定义的版本范围&#xff08;如 ^1.0.0 或 ~2.3.4&#xff09;更新依赖包到最新兼容版本&#xff0c;但不会突破版本…...

C++:类和对象(二)

目录 const成员函数 1. 基本语法 2. const 成员函数的作用 (1) 保证对象不被修改 (2) 提高代码安全性 (3) 支持 const 对象 3. 示例 (1) 基本用法 (2) const 对象只能调用 const 成员函数 (3) mutable 成员变量 4. const 成员函数的重载 初始化列表 基本语法 为什…...

【Django】教程-2-前端-目录结构介绍

【Django】教程-1-安装创建项目目录结构介绍 3. 前端文件配置 3.1 目录介绍 在app下创建static文件夹, 是根据setting中的配置来的 STATIC_URL ‘static/’ templates目录&#xff0c;编写HTML模板&#xff08;含有模板语法&#xff0c;继承&#xff0c;{% static ‘xx’ …...

2025年渗透测试面试题总结-某快手-安全工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 快手-安全工程师 一、Linux提权技术&#xff1a;Dirty Cow漏洞深度解析 1.1 漏洞技术原理 1.2 漏洞影…...

数据结构之栈

目录 1 简介 2 栈的基本概念 3 代码实现 4 代码解析&#xff08;部分&#xff09; 4.1 初始化栈 4.2 入栈 4.3 出栈 4.4 只读获取栈顶元素&#xff08;peek&#xff09; 4.5 判断是否为空 4.6 获取栈大小 4.7 十进制转换为二进制 5 核心操作分析 6 总结 1 简介 栈…...

【AndroidRTC-10】webrtc是如何确定双端的编解码类型?

Android-RTC系列软重启&#xff0c;改变以往细读源代码的方式 改为 带上实际问题分析代码。增加实用性&#xff0c;方便形成肌肉记忆。同时不分种类、不分难易程度&#xff0c;在线征集问题切入点。 问题&#xff1a;webrtc-android是如何确定编解码类型&#xff0c;如何调整视…...

深度求索(DeepSeek):以AI之力重塑医疗未来

目录 一、智能诊断&#xff1a;打破医疗认知的“分辨率极限” 二、药物研发&#xff1a;重构分子世界的“造物逻辑” 三、医疗资源重构&#xff1a;打造分级诊疗的“神经中枢” 四、健康管理&#xff1a;编织个体化医学的“防护网” 五、伦理与进化&#xff1a;构建医疗AI…...

【HTML 基础教程】HTML 属性

HTML 属性 属性是 HTML 元素提供的附加信息。 属性通常出现在 HTML 标签的开始标签中&#xff0c;用于定义元素的行为、样式、内容或其他特性。 属性总是以 name"value" 的形式写在标签内&#xff0c;name 是属性的名称&#xff0c;value 是属性的值。 HTML 属性 …...

macOS 制作dmg磁盘映像安装包

制作dmg磁盘影像安装包需要准备一下材料&#xff1a; 1. 导出的APP 2. 背景图片 3. 应用程序替身 前两种材料很容易得到。 下面介绍一下 应用程序替身制作过程&#xff1a; Finder —> 选中 应用程序 --> 找到顶部菜单栏中 的 前往 ----> 选择上层文件夹选中应用程…...

Appium中元素定位之一组元素定位API

应用场景 和定位一个元素相同&#xff0c;但如果想要批量的获取某个相同特征的元素&#xff0c;使用定位一组元素的方式更加方便 在 Appium 中定位一组元素的 API 与定位单个元素的 API 类似&#xff0c;但它们返回的是一个元素列表&#xff08;List<MobileElement>&am…...

webstorm中element-ui标签无法跳转源码

原本用的webstorm2019,之前的项目开发时切实体验过跳转element-ui源码&#xff0c;觉得很香。 更新了webstorm至2024&#xff0c;居然不行了&#xff0c;能弹出来提示&#xff0c;但就是找不到定义。 不知道是不是2024版本的问题&#xff0c;node_moudles不管我是否手动添加exc…...

【蓝桥杯】算法笔记1

1.暴力枚举 给定一个正整数n,请找出所有满足a + b = n的整数对(a, b),其中a和b都是正整数,且a ≤ b。 输入格式:一个正整数n (1 ≤ n ≤ 10⁶) 输出格式:所有符合条件的(a, b)对,每行一对,按a的升序排列。如果没有符合条件的对,输出"No solution"。 问题分…...

Pytorch学习笔记(十一)Learning PyTorch - What is torch.nn really

这篇博客瞄准的是 pytorch 官方教程中 Learning PyTorch 章节的 What is torch.nn really? 部分。主要是教你如何一步一步将最原始的代码进行重构至pytorch标准的代码&#xff0c;如果你已经熟悉了如何使用原始代码以及pytorch标准形式构建模型&#xff0c;可以跳过这一篇。 …...

OpenGL ES 2.0与OpenGL ES 3.1的区别

如果硬件支持且需要更高质量的图形效果&#xff0c;推荐3.1&#xff1b;如果兼容性和开发简便更重要&#xff0c;且效果需求不高&#xff0c;2.0更合适。不过现代车载系统可能越来越多支持3.x版本&#xff0c;所以可能倾向于使用3.1&#xff0c;但具体情况还需调查目标平台的硬…...

【Unity3D脚本与系统设计6】鼠标触摸超时待机实现

实现步骤 在Unity中实现一个功能&#xff0c;当鼠标或触摸超过一定时间没有操作时&#xff0c;自动返回待机界面。 检测输入 首先&#xff0c;我需要检测用户的输入&#xff0c;无论是鼠标还是触摸。Unity的Input系统可以检测到鼠标和触摸事件&#xff0c;比如Input.GetAxis…...

SpringMVC 入门教程

一、SpringMVC 简介 SpringMVC 是基于 MVC 设计模式的轻量级 Web 框架&#xff0c;核心功能包括&#xff1a; 请求分发&#xff1a;通过 DispatcherServlet 统一处理请求。注解驱动&#xff1a;使用 Controller、RequestMapping 简化开发。视图解析&#xff1a;支持 JSP、Thy…...

矿山自动化监测解决方案

1.行业现状 为贯彻落实《中共中央国务院关于推进安全生产领域改革发展的意见》《“十四五”矿山安全生产规划》&#xff08;应急〔2022〕64号&#xff09;、《国务院安委会办公室关于加强矿山安全生产工作的紧急通知》&#xff08;安委办〔2021〕3号&#xff09;等有关工作部署…...

el-table + el-pagination 前端实现分页操作

el-table el-pagination 前端实现分页操作 后端返回全部列表数据&#xff0c;前端进行分页操作 html代码 <div><el-table :data"tableData" border><el-table-column label"序号" type"index" width"50" /><el…...

NIO ByteBuffer 总结

目录 基本概念创建 ByteBuffer核心属性关键方法切换模式读写操作压缩数据 基本概念 java.nio.ByteBuffer 是 Java NIO 中一个核心类&#xff0c; 用于高效处理二进制数据的读写操作。应用于通道&#xff08;Channel&#xff09;的I/O操作。作用&#xff1a; 数据缓冲&#xf…...

华为hcia——Datacom实验指南——配置IPv4静态路由,默认路由和浮动静态路由

什么是IPv4 IPv4静态路由&#xff0c;是手动配置的&#xff0c;不会随着网络拓扑的变化而变化&#xff0c;所配置的路由信息也不会在网络中传播&#xff0c;所以它主要运用在小型网络或者作为动态路由的补充。 IPv4的配置 配置的命令很简单 IP route-static &#xff08;目…...

Git入门——常用指令汇总

以下是一份精心整理的 Git常用指令速查表&#xff0c;基本覆盖日常开发使用场景&#xff0c;建议收藏备用&#x1f447; &#x1f527; 环境配置 指令作用git config --global user.name "你的名字"设置全局用户名git config --global user.email "你的邮箱&qu…...

深入解析 MyBatis-Plus 批量操作:原理、实现与性能优化

引言 在高并发、大数据量场景下,批量数据库操作是提升系统性能的核心手段之一。本文以 MyBatis-Plus 为例,深入剖析 批量更新 和 自定义批量插入 的实现原理,并结合实战代码与性能测试,揭示其在高性能场景下的应用价值。 批量更新:动态 SQL 的极致运用 原理与 SQL 生成…...

2025年成都市双流区农业科技试验示范基地建设方案申报条件材料和补贴程序、时间安排

成都市双流区2025年农业科技试验示范基地建设方案申报条件材料和补贴程序、时间安排如下&#xff0c;需要申报的可指导&#xff01; 一、成都市双流区农业科技试验示范基地申报 &#xff08;一&#xff09;基地建设数量。2025年建设农业科技试验示范基地2个。 &#xff08;二…...

8路CXP相机采集系统介绍

8xCXP相机采集系统介绍 目录 1 系统概述 4 2 硬件架构 5 2.1 FPGA处理单元 5 2.2 CXP接口层 6 2.3 CXP相机说明与使用要求 7 2.4 SSI控制器板 8 3 FPGA方案 9 3.1 FPGA实现 9 3.2 Block Design说明 10 4 软件方案 14 4.1 嵌入式层 14 4.2 上位机软件&#xff08;C…...

vue2前端日志数据存储,推荐(IndexedDB)

前言&#xff1a;首先&#xff0c;我得回忆一下IndexedDB的基本概念和用法&#xff0c;确保自己理解正确。IndexedDB是一个浏览器内置的数据库&#xff0c;允许存储大量结构化数据&#xff0c;支持事务和索引查询&#xff0c;适合需要离线存储的应用场景。 接下来&#xff0c;用…...

onedav一为导航批量自动化导入网址(完整教程)

OneNav作为一个功能强大的导航工具,支持后台管理、加密链接、浏览器书签批量导入等功能,能够帮助用户轻松打造专属的导航页面。今天,我将为大家详细介绍如何实现OneNav导航站的批量自动化导入网址。 1、建立要批量导入的表格 格局需要创建表格,表格的要求是一定要有需要,…...

Ubuntu Linux安装PyQt5并配置Qt Designer

一 安装 PyQt5 借助 apt 包管理器来安装 PyQt5 及其相关的开发工具&#xff1a; sudo apt install python3-pyqt5 pyqt5-dev-tools 假如报错&#xff0c; You might want to run apt --fix-broken install to correct these. 直接执行&#xff1a; sudo apt --fix-…...

无人机螺旋桨平衡标准

螺旋桨平衡是确保无人机(UAV)平稳运行、可靠性和使用寿命的关键过程。螺旋桨的不平衡会导致振动、噪音&#xff0c;并加速关键部件的磨损&#xff0c;从而对飞行性能产生负面影响。 ISO 21940-11:2016标准为旋翼平衡提供了一个广泛引用的框架&#xff0c;定义了可接受的不平衡…...

基于MCP协议的多模态模型优化在医疗3D打印精密人工关节制造中的研究

一、引言 1.1 研究背景与意义 在全球人口老龄化趋势愈发明显的当下,诸如骨关节炎、类风湿性关节炎这类关节疾病的发病率不断攀升,进而使得人工关节置换手术的需求呈现出激增态势。人工关节置换手术作为治疗终末期关节疾病的有效手段,能够显著缓解患者疼痛,提升关节功能与生…...

ESLint报错:Could not find config file.

如果你的ESLint的版本大于 8&#xff0c;同时使用 .eslinrc.js 和 .eslintignore 作为配置文件&#xff0c;且目前用的是 VSCODE &#xff0c;就有可能遇到报错&#xff1a; Could not find config file. 这个是因为 VSCode 中 ESLint 插件的配置 eslint.useFlatConfig 的问题…...

npm install 卡在创建项目:sill idealTree buildDeps

参考&#xff1a; https://blog.csdn.net/PengXing_Huang/article/details/136460133 或者再执行 npm install -g cnpm --registryhttps://registry.npm.taobao.org 或者换梯子...

drizzleDumper:基于内存搜索的Android脱壳工具

一、工具介绍 drizzleDumper 是一款基于内存搜索的 Android 脱壳工具&#xff0c;主要用于从加固的 Android 应用程序中提取原始的 DEX 文件。它通过分析应用程序运行时的内存&#xff0c;定位并提取被加固的 DEX 文件&#xff0c;从而帮助开发者、安全研究人员进行逆向工程和…...

信号处理中的窗

窗函数&#xff08;Window Function&#xff09;是一种在信号处理中常用的工具&#xff0c;用于对信号进行截断和加权处理。它在频谱分析、滤波器设计以及信号处理的许多其他领域中都发挥着重要作用。 窗函数的基本概念 窗函数本质上是一个有限长度的序列&#xff0c;通常用于…...

FFmpeg学习:AVPacket结构体

1.AVPacket结构体 FFmpeg中用于封装一帧的编码数据的结构体&#xff08;比如H264视频帧或者AAC音频帧&#xff09;&#xff0c;主要用于编解码过程中数据的载体&#xff0c;使用av_read_frame()读取获得&#xff0c;或者使用avcodec_send_packet()进行解码&#xff0c;与AVFra…...

34.[前端开发-JavaScript基础]Day11-王者轮播图-书籍购物车-BOM对象-JSON

1 认识BOM操作 认识BOM 2 全局对象window window对象 window对象的作用 window常见的属性 window常见的方法 3 事件对象event window常见的事件 4 location、history location对象常见的属性 Location对象常见的方法 URLSearchParams history对象常见属性和方法 5 navigato…...

FLEXlm如何通过web调用

FLEXlm 是一种流行的软件许可管理工具&#xff0c;广泛用于各种软件产品的授权管理。它支持多种协议&#xff0c;包括传统的服务器-客户端模式和一些基于网络的解决方案。如果你想通过 Web 接口调用 FLEXlm 许可证服务器&#xff0c;你可以通过以下几种方式实现&#xff1a; 使…...

深度解析Spring Boot可执行JAR的构建与启动机制

一、Spring Boot应用打包架构演进 1.1 传统JAR包与Fat JAR对比 传统Java应用的JAR包在依赖管理上存在明显短板&#xff0c;依赖项需要单独配置classpath。Spring Boot创新的Fat JAR&#xff08;又称Uber JAR&#xff09;解决方案通过spring-boot-maven-plugin插件实现了"…...

Zookeeper运维指南:服务端与客户端常用命令详解

#作者&#xff1a;任少近 文章目录 1 Zookeeper服务端常用命令2 Zookeeper客户端常用命令2.1Ls命令2.2创建节点create2.3Get命令2.4删除命令2.5修改命令 1 Zookeeper服务端常用命令 启动ZK服务: bin/zkServer.sh start # ./zkServer.sh startZooKeeper JMX enabled by defau…...

K8S学习之基础五十一:k8s部署jenkins

k8s部署jenkins 创建nfs共享目录&#xff0c; mkdir -p /data/v2 echo /data/v2 *(rw,no_root_squash) > /etc/exports exportfs -arv创建pv、pvc vi pv.yaml apiVersion: v1 kind: PersistentVolume metadata:name: jenkins-k8s-pv spec:capacity:storage: 1GiaccessMod…...

界面控件DevExpress WinForms v25.1 - 人工智能(AI)方面全新升级

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…...

基于动态光影融合的缺陷实时检测和材质量化方法,并且整合EventPS、VMNer和EvDiG

要完成基于动态光影融合的缺陷实时检测和材质量化方法&#xff0c;并且整合EventPS、VMNer和EvDiG&#xff0c;是一个复杂且综合性的任务。以下是一个大致的实现步骤和代码示例&#xff0c;不过要完整完成论文和所有实验还需要大量的细化和调整。 整体思路 数据加载与预处理&…...

关于我对接了deepseek之后部署到本地将数据存储到mysql的过程

写在前面 今天写一下使用nodejs作为服务端&#xff0c;vue作为客户端&#xff0c;mysql的数据库&#xff0c;对接deepseek的全过程&#xff0c;要实现一个很简单的效果就是&#xff0c;可以自由的询问&#xff0c;然后可以将询问的过程存储到mysql的数据库中。 文档对接 deeps…...