您的当前位置:首页正文

SpringBoot整合redisson实现分布式锁

来源:华佗小知识

文章内容过长,文末有彩蛋!希望大家多多关注

最近面试总是会被问到有没有用过分布式锁、redis锁,由于平时很少接触到,所以只能很无奈的回答“没有”。回来之后就恶补了一下,本文主要做下记录,通过SpringBoot整合redisson来实现分布式锁,并结合demo测试结果。

首先看下大佬总结的图

下面上代码啦

增加依赖

<!--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>

配置

spring:

# redis

redis:

host: 47.103.5.190

port: 6379

redisson:

# 配置单点模式

config: classpath:redisson-single-dev.yml

增加 redisson-single-dev.yml 配置

# 单节点配置

singleServerConfig:

# 连接空闲超时,单位:毫秒

idleConnectionTimeout: 10000

pingTimeout: 1000

# 连接超时,单位:毫秒

connectTimeout: 10000

# 命令等待超时,单位:毫秒

timeout: 3000

# 命令失败重试次数,如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。

# 如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。

retryAttempts: 3

# 命令重试发送时间间隔,单位:毫秒

retryInterval: 1500

# 重新连接时间间隔,单位:毫秒

reconnectionTimeout: 3000

# 执行失败最大次数

failedAttempts: 3

# 密码

password: null

# 单个连接最大订阅数量

subscriptionsPerConnection: 5

# 客户端名称

clientName: null

# 节点地址

address: redis://47.103.5.190:6379

# 发布和订阅连接的最小空闲连接数

subscriptionConnectionMinimumIdleSize: 1

# 发布和订阅连接池大小

subscriptionConnectionPoolSize: 50

# 最小空闲连接数

connectionMinimumIdleSize: 32

# 连接池大小

connectionPoolSize: 64

# 数据库编号

database: 0

# DNS监测时间间隔,单位:毫秒

dnsMonitoringInterval: 5000

# 线程池数量,默认值: 当前处理核数量 * 2

threads: 0

# Netty线程池数量,默认值: 当前处理核数量 * 2

nettyThreads: 0

# 编码

codec: !<org.redisson.codec.JsonJacksonCodec> {}

# 传输模式

transportMode : "NIO"

封装工具类

/**

* redis分布式锁帮助类

* @author yangzhilong

*

*/

@Component

public class RedisLockUtil {

@Autowired

private DistributedLocker locker;

private static DistributedLocker distributedLocker;

@PostConstruct

private void init() {

distributedLocker = locker;

}

/**

* 加锁

* @param lockKey

* @return

*/

public static RLock lock(String lockKey) {

return distributedLocker.lock(lockKey);

}

/**

* 释放锁

* @param lockKey

*/

public static void unlock(String lockKey) {

distributedLocker.unlock(lockKey);

}

/**

* 释放锁

* @param lock

*/

public static void unlock(RLock lock) {

distributedLocker.unlock(lock);

}

/**

* 带超时的锁

* @param lockKey

* @param timeout 超时时间 单位:秒

*/

public static RLock lock(String lockKey, int timeout) {

return distributedLocker.lock(lockKey, timeout);

}

/**

* 带超时的锁

* @param lockKey

* @param unit 时间单位

* @param timeout 超时时间

*/

public static RLock lock(String lockKey, int timeout,TimeUnit unit ) {

return distributedLocker.lock(lockKey, unit, timeout);

}

/**

* 尝试获取锁

* @param lockKey

* @param waitTime 最多等待时间

* @param leaseTime 上锁后自动释放锁时间

* @return

*/

public static 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 static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {

return distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);

}

}

底层封装:

/**

* @author gourd

*/

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);

}

/**

* @author gourd

*/

@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();

}

}

测试

模拟并发测试

/**

* redis分布式锁控制器

* @author gourd

* @since 2019-07-30

*/

@RestController

@Api(tags = "redisson", description = "redis分布式锁控制器" )

@RequestMapping("/redisson" )

@Slf4j

public class RedissonLockController {

/**

* 锁测试共享变量

*/

private Integer lockCount = 10;

/**

* 无锁测试共享变量

*/

private Integer count = 10;

/**

* 模拟线程数

*/

private static int threadNum = 10;

/**

* 模拟并发测试加锁和不加锁

* @return

*/

@GetMapping("/test")

@ApiOperation(value = "模拟并发测试加锁和不加锁")

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();

}

}

}

调用接口后打印值:

测试结果

根据打印结果可以明显看到,未加锁的count--后值是乱序的,而加锁后的结果和我们预期的一样。由于条件问题没办法测试分布式的并发。只能模拟单服务的这种并发,但是原理是一样,希望对大家有帮助。如有错误之处,欢迎指正。

来源:csdn

需要《深入实践springboot.pdf》资料,多种资料及面试题,私信“资料”获取

还有面试宝典《Java核心知识点整理.pdf》“,覆盖了JVM、锁、高并发、反射、Spring原理等多种面试资料!!