本文主要通过 SpringBoot 整合 redisson 来实现分布式锁,并结合 demo 测试结果。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-redission</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-redission</name>
<description>springboot-redission</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.10.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring:
# redis
redis:
host: 127.0.0.1
port: 6379
jedis:
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 100
# 连接池中的最小空闲连接
max-idle: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接超时时间(毫秒)
timeout: 5000
# 默认是索引为0的数据库
database: 0
package com.example.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
/**
* redisson配置
*/
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password:}")
private String password;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
//单节点
config.useSingleServer().setAddress("redis://" + host + ":" + port);
if (StringUtils.isEmpty(password)) {
config.useSingleServer().setPassword(null);
} else {
config.useSingleServer().setPassword(password);
}
return Redisson.create(config);
}
}
package com.example.utils;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import java.util.concurrent.TimeUnit;
public interface DistributedLocker {
RLock lock(String lockKey);
RLock lock(String lockKey, int timeout);
RLock lock(String lockKey, TimeUnit unit, int timeout);
boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);
void unlock(String lockKey);
void unlock(RLock lock);
RCountDownLatch getCountDownLatch(String name);
RSemaphore getSemaphore(String name);
}
package com.example.utils;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLocker implements DistributedLocker {
@Autowired
private RedissonClient redissonClient;
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
@Override
public RLock lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return lock;
}
@Override
public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
@Override
public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.unlock();
}
@Override
public RCountDownLatch getCountDownLatch(String name) {
return redissonClient.getCountDownLatch(name);
}
@Override
public RSemaphore getSemaphore(String name) {
return redissonClient.getSemaphore(name);
}
}
package com.example.utils;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* redis分布式锁帮助类
*
* @author gourd
*/
@Component
public class RedisLockUtil {
@Autowired
private DistributedLocker distributedLocker;
/**
* 加锁
*
* @param lockKey
* @return
*/
public RLock lock(String lockKey) {
return distributedLocker.lock(lockKey);
}
/**
* 释放锁
*
* @param lockKey
*/
public void unlock(String lockKey) {
distributedLocker.unlock(lockKey);
}
/**
* 释放锁
*
* @param lock
*/
public void unlock(RLock lock) {
distributedLocker.unlock(lock);
}
/**
* 带超时的锁
*
* @param lockKey
* @param timeout 超时时间 单位:秒
*/
public RLock lock(String lockKey, int timeout) {
return distributedLocker.lock(lockKey, timeout);
}
/**
* 带超时的锁
*
* @param lockKey
* @param unit 时间单位
* @param timeout 超时时间
*/
public RLock lock(String lockKey, int timeout, TimeUnit unit) {
return distributedLocker.lock(lockKey, unit, timeout);
}
/**
* 尝试获取锁
*
* @param lockKey
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
return distributedLocker.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
}
/**
* 尝试获取锁
*
* @param lockKey
* @param unit 时间单位
* @param waitTime 最多等待时间
* @param leaseTime 上锁后自动释放锁时间
* @return
*/
public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
return distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);
}
/**
* 获取计数器
*
* @param name
* @return
*/
public RCountDownLatch getCountDownLatch(String name) {
return distributedLocker.getCountDownLatch(name);
}
/**
* 获取信号量
*
* @param name
* @return
*/
public RSemaphore getSemaphore(String name) {
return distributedLocker.getSemaphore(name);
}
}
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootRedissionApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootRedissionApplication.class, args);
}
}
模拟并发测试
package com.example.controller;
import com.example.utils.RedisLockUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* redis分布式锁控制器
*/
@RestController
@RequestMapping("/redisson")
@Slf4j
public class RedissonLockController {
@Autowired
private RedisLockUtil redisLockUtil;
/**
* 锁测试共享变量
*/
private Integer lockCount = 10;
/**
* 无锁测试共享变量
*/
private Integer count = 10;
/**
* 模拟线程数
*/
private static int threadNum = 10;
/**
* 模拟并发测试加锁和不加锁
*
* @return
*/
@GetMapping("/test")
public void lock() {
// 计数器
final CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < threadNum; i++) {
MyRunnable myRunnable = new MyRunnable(countDownLatch);
Thread myThread = new Thread(myRunnable);
myThread.start();
}
// 释放所有线程
countDownLatch.countDown();
}
/**
* 加锁测试
*/
private void testLockCount() {
String lockKey = "lock-test";
try {
// 加锁,设置超时时间2s
redisLockUtil.lock(lockKey, 2, TimeUnit.SECONDS);
lockCount--;
log.info("lockCount值:" + lockCount);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
// 释放锁
redisLockUtil.unlock(lockKey);
}
}
/**
* 无锁测试
*/
private void testCount() {
count--;
log.info("count值:" + count);
}
public class MyRunnable implements Runnable {
/**
* 计数器
*/
final CountDownLatch countDownLatch;
public MyRunnable(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
// 阻塞当前线程,直到计时器的值为0
countDownLatch.await();
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
// 无锁操作
testCount();
// 加锁操作
testLockCount();
}
}
}
调用接口后打印值:http://localhost:8080/redisson/test
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-15] c.e.controller.RedissonLockController : count值:4
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-14] c.e.controller.RedissonLockController : count值:5
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-18] c.e.controller.RedissonLockController : count值:6
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-11] c.e.controller.RedissonLockController : count值:7
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-16] c.e.controller.RedissonLockController : count值:1
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-12] c.e.controller.RedissonLockController : count值:5
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-13] c.e.controller.RedissonLockController : count值:4
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-19] c.e.controller.RedissonLockController : count值:2
2022-08-16 19:52:02.956 INFO 13112 --- [ Thread-20] c.e.controller.RedissonLockController : count值:0
2022-08-16 19:52:02.955 INFO 13112 --- [ Thread-17] c.e.controller.RedissonLockController : count值:3
2022-08-16 19:52:03.074 INFO 13112 --- [ Thread-11] c.e.controller.RedissonLockController : lockCount值:9
2022-08-16 19:52:03.100 INFO 13112 --- [ Thread-12] c.e.controller.RedissonLockController : lockCount值:8
2022-08-16 19:52:03.106 INFO 13112 --- [ Thread-17] c.e.controller.RedissonLockController : lockCount值:7
2022-08-16 19:52:03.116 INFO 13112 --- [ Thread-13] c.e.controller.RedissonLockController : lockCount值:6
2022-08-16 19:52:03.139 INFO 13112 --- [ Thread-14] c.e.controller.RedissonLockController : lockCount值:5
2022-08-16 19:52:03.149 INFO 13112 --- [ Thread-19] c.e.controller.RedissonLockController : lockCount值:4
2022-08-16 19:52:03.157 INFO 13112 --- [ Thread-16] c.e.controller.RedissonLockController : lockCount值:3
2022-08-16 19:52:03.163 INFO 13112 --- [ Thread-18] c.e.controller.RedissonLockController : lockCount值:2
2022-08-16 19:52:03.168 INFO 13112 --- [ Thread-20] c.e.controller.RedissonLockController : lockCount值:1
2022-08-16 19:52:03.179 INFO 13112 --- [ Thread-15] c.e.controller.RedissonLockController : lockCount值:0
测试结果:
根据打印结果可以明显看到,未加锁的 count-- 后值是乱序的,而加锁后的结果和我们预期的一样。
由于条件问题没办法测试分布式的并发,只能模拟单服务的这种并发,但是原理是一样,希望对大家有帮助。