springboot整合redisson分布式锁

synchronized是jvm级别的线程同步,当项目使用分布式、集群,就需要使用分布式锁

介绍

redisson锁

  1. 可重入锁(Reentrant Lock)
  2. 公平锁(Fair Lock)
  3. 联锁(MultiLock)
  4. 红锁(RedLock)
  5. 读写锁(ReadWriteLock)
  6. 信号量(Semaphore)
  7. 可过期性信号量(PermitExpirableSemaphore)
  8. 闭锁(CountDownLatch)

常用的锁

  1. 可重入锁加锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    RLock lock = redissonClient.getLock("key");
    // 加锁,一直等待加锁成功。
    // 默认加锁30秒,如果当前线程处理时间过长,
    // redisson会在锁时间过了三分之二的时候将锁的时间重新设置为30秒(看门狗机制)
    lock.lock();
    try{
    // ...
    }final {
    lock.unlock();
    }
  2. 可重入锁尝试加锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    RLock lock = redissonClient.getLock("key");
    // 尝试加锁,最多等待时间10秒,如果10秒内未获取到锁返回false
    boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
    if(isLock){
    try{
    // ...
    }final {
    lock.unlock();
    }
    }
  3. 联锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    RLock lock1 = redissonClient.getLock("key1");
    RLock lock2 = redissonClient.getLock("key2");
    RLock multiLock = redissonClient.getMultiLock(lock1, lock2);
    multiLock.lock();
    try{
    // ...
    }final {
    multiLock.unlock();
    }

使用

引入依赖

1
2
3
4
5
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.14.1</version>
</dependency>

配置

  1. yml配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    spring:
    redis:
    host: localhost
    port: 6379
    database: 0
    password:
    timeout: 3000ms
    redisson:
    # 使用config配置,大部分使用默认配置。具体配置查看https://github.com/redisson/redisson/wiki/2.-Configuration
    config: |
    singleServerConfig:
    address: "redis://${spring.redis.host}:${spring.redis.port}"
    password: ${spring.redis.password}
    database: ${spring.redis.database}
    codec: !<org.redisson.codec.JsonJacksonCodec> {}
    # 也可以通过指定redisson配置文件的位置,两种方式任选一种
    # config: classpath:redisson.yml
  2. redisson.yml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#更多配置详情查看 https://github.com/redisson/redisson/wiki/2.-Configuration
singleServerConfig:
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
#密码
password:
subscriptionsPerConnection: 5
clientName: null
#redis地址
address: "redis://localhost:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 32
connectionPoolSize: 64
#索引库
database: 0
#不注释会注入bean失败
# dnsMonitoring: false
# dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: NIO

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@RestController
public class TestController {

@Autowired
private RedissonClient redissonClient;
@Autowired
private UserMapper userMapper;

/**
* 模拟添加用户
*/
@PostMapping("addUser")
public String addUser(@RequestBody User user){
// 获取锁,以username为锁名称。这样用户名相同的数据添加时,无法并行处理。
// 相同username的第二次请求需要等到第一次请求的锁释放后才可以继续执行
RLock lock = redissonClient.getLock(user.getUsername());
// 加锁,默认锁30秒,如果当前线程处理时间过长。
// redisson会在锁时间过了三分之二的时候将锁的时间重新设置为30秒(看门狗机制)
lock.lock();
//将需要锁住的代码try起来,并在finally中释放锁
try {
// 判断用户账号是否重复
SysUser sysUser = userMapper.selectByUsername(user.getUsername());
// 如果用户存在,抛出异常
if (sysUser != null) {
throw new RuntimeException("用户已经存在");
}
// 模拟线程不安全的情况
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}

// 不重复则添加用户
userMapper.insert(user);
}finally {
// 释放锁
lock.unlock();
}
return "success";
}
}