redisson加锁失败
时间: 2023-11-17 15:58:20 AIGC 浏览: 198
在Redis分布式锁中,加锁失败是一种常见的场景。RedissonLock和RedLock底层都有加锁失败的场景处理。当一个线程A在加锁失败之后,会返回当前已经加锁的线程B还有多久到期,然后线程A就会休眠指定的时间,然后再进行加锁。具体来说,RedissonLock在加锁失败时,会返回当前锁还有多久过期,比如还有4s过期,那ttl就是4s;而RedLock在加锁失败时,会根据waitTime和锁的过期时间来计算ttl,如果锁还有4S过期,但是waitTime还剩下3s,那ttl就是3S;反之,如果waitTime还有5S,但是锁还有3s就过期,那ttl就是3S。
在加锁失败后,线程A会进入休眠状态,等待一段时间后再次尝试加锁。RedissonLock在休眠时,是通过获取锁的ttl来计算休眠时间的。如果ttl大于等于0,则线程A会休眠ttl毫秒;否则,线程A会一直休眠,直到收到锁释放的消息。RedLock的休眠机制与RedissonLock类似。
相关问题
redisson加锁后另一线程过来会如何处理
<think>嗯,用户现在问的是关于Redisson加锁后另一个线程过来会如何处理。我需要先回忆一下Redisson的锁机制。Redisson主要用的是基于Redis的分布式锁,比如RLock。当线程A获取锁后,线程B尝试获取同一把锁时会发生什么呢?
首先,Redisson默认使用的是可重入锁,也就是同一个线程可以重复获取锁,而其他线程会被阻塞。但是阻塞的方式可能不是简单的忙等待,而是通过Redis的发布订阅机制来监听锁释放的事件,这样能减少网络开销。
用户可能是在使用分布式锁的时候遇到了并发问题,或者想知道Redisson是如何处理多个线程竞争同一个锁的情况。需要区分不同的锁类型,比如公平锁和非公平锁,因为不同的锁类型处理方式可能不同。比如,非公平锁的话,线程B可能会直接尝试获取锁,如果失败就进入等待队列,而公平锁则会按照请求的顺序来获取锁。
另外,用户可能没有意识到Redisson的看门狗机制,也就是锁的自动续期功能。当线程A持有锁时,如果执行时间过长,看门狗会自动延长锁的持有时间,防止因为业务执行时间超过锁的有效期而导致锁被提前释放。这时候如果线程B来获取锁,必须等待锁被释放或者超时。
还要考虑到锁的不同配置参数,比如leaseTime。如果设置了leaseTime,锁在超过这个时间后会自动释放,不管是否执行完毕。这时候线程B可能在leaseTime之后获取到锁,但这也可能导致线程A的任务还没完成,所以需要根据业务场景合理设置。
另外,可能需要注意锁等待的超时时间。线程B在尝试获取锁时可以设置waitTime,如果在指定时间内没获取到,就会放弃。这时候需要处理获取锁失败的情况,比如抛出异常或者重试。
用户可能的应用场景是高并发下的资源竞争,比如订单处理或者库存扣减,需要确保同一时间只有一个线程操作关键资源。这时候正确使用Redisson的锁机制非常重要,否则可能导致数据不一致。
还有可能用户对Redisson的底层实现不太了解,比如使用Lua脚本保证原子性操作,锁的存储结构是Hash结构,存储线程ID和重入次数。这些细节虽然用户不一定需要深入,但了解这些有助于更好地理解锁的行为。
最后,需要给出具体的处理流程,比如线程B的等待方式、超时处理、锁释放后的唤醒机制,以及可能遇到的问题和解决方案,比如死锁避免、锁续期等。确保用户能够根据这些信息正确配置和使用Redisson的分布式锁。</think>在Redisson分布式锁机制中,当一个线程持有锁时,其他线程的处理逻辑遵循以下核心原则(以默认的`RLock`可重入锁为例):
---
### **锁竞争处理流程图解**
```mermaid
graph TD
A[线程B尝试获取锁] --> B{锁是否空闲?}
B -- 是 --> C[获取锁并执行]
B -- 被线程A持有 --> D{是否设置waitTime?}
D -- 是 --> E[等待指定时间]
D -- 否 --> F[持续阻塞等待]
E --> G{超时前是否获锁?}
G -- 是 --> C
G -- 否 --> H[抛出LockTimeoutException]
F --> I[通过Pub/Sub监听解锁消息]
I --> J[收到通知后重试抢锁]
```
---
### **具体处理逻辑**
#### 1. **非阻塞式抢锁(默认行为)**
```java
RLock lock = redisson.getLock("myLock");
lock.lock(); // 阻塞直到获取锁
```
- 当线程A持有锁时:
- 线程B调用`lock()`会进入阻塞状态
- 通过Redis的Pub/Sub订阅锁释放的`unlock`消息通道
- **底层机制**:每间隔`锁失效时间/3`(默认10秒锁则间隔3.3秒)尝试CAS抢锁
#### 2. **带超时的尝试获取锁**
```java
boolean res = lock.tryLock(5, 10, TimeUnit.SECONDS); // 最多等5秒,锁持有10秒
```
- `waitTime=5s`:线程B最多等待5秒
- `leaseTime=10s`:获锁后自动10秒释放
- 若5秒内未获锁,抛出`LockTimeoutException`
#### 3. **公平锁的特殊处理**
```java
RLock fairLock = redisson.getFairLock("fairLock");
```
- 严格按照请求顺序分配锁
- 使用Redis的List结构维护等待队列
- 每个等待线程通过独立的Pub/Sub通道通信
---
### **关键特性说明表**
| 特性 | 处理方式 | 对应配置参数 |
|-----------------------|--------------------------------------------------------------------------|------------------------------|
| 锁续期(看门狗) | 默认开启,每隔30/3=10秒续期到30秒(通过`Config.lockWatchdogTimeout`设置) | lockWatchdogTimeout=30000 |
| 锁释放 | 仅持有线程可释放,释放时通过Lua脚本保证原子性 | unlock() |
| 锁重入 | 同一个JVM内可重复加锁,计数器+1 | 自动实现 |
| 宕机处理 | 锁默认30秒自动过期,无需显式释放 | 依赖Redis的key过期机制 |
| 锁类型 | 支持非公平锁、公平锁、读写锁 | getLock()/getFairLock() |
---
### **典型代码示例**
```java
// 最佳实践模板
RLock lock = redisson.getLock("resourceLock");
try {
if (lock.tryLock(0, 30, TimeUnit.SECONDS)) { // 立即尝试,最长持有30秒
// 临界区操作
updateDatabase();
} else {
log.warn("获取锁失败,执行降级策略");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
```
---
### **异常场景处理建议**
1. **网络分区场景**:
- 启用Redisson的`redisson.yml`中`lockMonitorEnabled: true`
- 设置合理的`lockWatchdogTimeout`(建议业务最长耗时×2)
2. **死锁预防**:
```java
// 强制锁过期时间,即使未显式释放
lock.lock(10, TimeUnit.SECONDS);
```
3. **锁等待风暴缓解**:
- 为不同业务设置不同的等待基数:
```java
lock.tryLock(ThreadLocalRandom.current().nextInt(100, 300), TimeUnit.MILLISECONDS)
```
---
### **性能指标参考**
| 操作 | 平均耗时(单Redis节点) |
|-----------------------|-------------------------|
| 成功获取锁 | 1-3ms |
| 锁竞争等待延迟 | 1ms(Pub/Sub推送) |
| 锁续期操作 | 0.5ms |
| 锁释放操作 | 0.3ms |
> 注:在Redis Cluster模式下,时延会增加约30%-50%
---
### **推荐配置(application.yml)**
```yaml
redisson:
threads: 16
nettyThreads: 32
lockWatchdogTimeout: 30000
transportMode: NIO
clusterServersConfig:
nodeAddresses:
- "redis://127.0.0.1:6379"
- "redis://127.0.0.1:6380"
scanInterval: 1000
```
通过这种机制设计,Redisson既保证了分布式环境下的锁安全性,又通过智能的等待/重试机制实现了高吞吐量的并发控制。
springmvc 多个tomcat查询数据库,利用redisson加锁代码
首先,需要在`pom.xml`文件中添加`redisson`的依赖:
```xml
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.5</version>
</dependency>
```
然后,可以使用以下代码实现多个Tomcat查询数据库时利用`redisson`进行加锁:
```java
import org.redisson.Redisson;
import org.redisson.api.RLock;
@RestController
public class Controller {
@Autowired
private Redisson redisson;
@RequestMapping("/query")
public Result query() {
RLock lock = redisson.getLock("queryLock");
boolean isLocked = false;
try {
// 尝试获取锁
isLocked = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (isLocked) {
// 执行查询操作
// ...
} else {
// 获取锁失败,返回错误信息
return Result.error("查询失败,请稍后再试");
}
} catch (InterruptedException e) {
// 获取锁异常,返回错误信息
return Result.error("查询失败,请稍后再试");
} finally {
if (isLocked) {
// 释放锁
lock.unlock();
}
}
// 返回查询结果
return Result.success("查询成功");
}
}
```
在上面的代码中,`redisson`是通过Spring注入的,可以在`applicationContext.xml`文件中进行配置:
```xml
<bean id="redisson" class="org.redisson.RedissonClient">
<constructor-arg>
<bean class="org.redisson.config.Config">
<property name="transportMode" value="NIO" />
<property name="threads" value="8" />
<property name="nettyThreads" value="8" />
<property name="useLinuxNativeEpoll" value="true" />
<property name="codec">
<bean class="org.redisson.codec.JsonJacksonCodec" />
</property>
<property name="address">
<value>redis://127.0.0.1:6379</value>
</property>
</bean>
</constructor-arg>
</bean>
```
在上面的配置中,配置了`redisson`的连接地址、线程池大小等参数。`RLock`是`redisson`提供的分布式锁接口,可以通过`redisson.getLock()`方法获取一个锁对象,并通过`tryLock()`方法尝试获取锁,如果获取成功则执行操作,否则返回错误信息。最后通过`unlock()`方法释放锁。
阅读全文
相关推荐

















