SpringBoot系列-redis分布式锁实现
SpringBoot整合Redis分布式锁教程
章目录
提示:从今天起,撸起袖子跟着我加油干
提示:如有疑问请私信联系
前言
使用Spring Boot 可以非常方便、快速搭建项目,使我们不用关心框架之间的兼容性,适用版本等各种问题,我们想使用任何东西,仅仅添加一个配置就可以。
提示:以下是本篇文章正文内容,下面案例可供参考
一、技术介绍
1.Redis是什么?
Redis 是一个高性能的key-value数据库,redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。
2.分布式锁是什么?
分布式锁是为了防止分布式系统中的多个进程之间相互干扰的一种分布式协调技术。
二、使用步骤
1.引入maven库
代码如下(示例):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.4.1</version>
</dependency>
</dependencies>
2.封装Redis分布式锁工具类
代码如下(示例):
package com.hyh.redis.helper;
import com.hyh.utils.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;
/**
* redis分布式锁助手
*
* @Author: heyuhua
* @Date: 2020/1/6 17:15
*/
@Component
public class RedisDistributionLockHelper {
/**
* 日志
*/
private static final Logger LOG = LoggerFactory.getLogger(RedisDistributionLockHelper.class);
/*** 分布式锁key ***/
private static final String DISTRIBUTION_LOCK_KEY = "distribut_lock_";
/*** 分布式锁过期时间 ***/
private static final Integer EXPIRE_TIME = 30;
/*** 每次自旋睡眠时间 ***/
private static final Integer SLEEP_TIME = 50;
/*** 分布式锁自旋次数 ***/
private static final Integer CYCLES = 10;
/**
* redis模板调用类
*/
@Autowired
private RedisTemplate redisTemplate;
/**
* 初始化
*/
@PostConstruct
public void init() {
LOG.info("---redis分布式锁助手初始化----");
}
/**
* 加锁
*
* @param key 加锁唯一标识
* @param value 释放锁唯一标识(建议使用线程ID作为value)
*/
public void lock(String key, String value) {
lock(key, value, EXPIRE_TIME);
}
/**
* 加锁
*
* @param key 加锁唯一标识
* @param value 释放锁唯一标识(建议使用线程ID作为value)
* @param timeout 超时时间(单位:S)
*/
public void lock(String key, String value, Integer timeout) {
Assert.isTrue(StringUtils.isNotBlank(key), "redis locks are identified as null.");
Assert.isTrue(StringUtils.isNotBlank(value), "the redis release lock is identified as null.");
int cycles = CYCLES;
// ----- 尝试获取锁,当获取到锁,则直接返回,否则,循环尝试获取
while (!tryLock(key, value, timeout)) {
// ----- 最多循环10次,当尝试了10次都没有获取到锁,抛出异常
if (0 == (cycles--)) {
LOG.error("redis try lock fail. key: {}, value: {}", key, value);
throw new RuntimeException("redis try lock fail.");
}
try {
TimeUnit.MILLISECONDS.sleep(SLEEP_TIME);
} catch (Exception e) {
LOG.error("history try lock error.", e);
}
}
}
/**
* 尝试获取锁
*
* @param key 加锁唯一标识
* @param value 释放锁唯一标识(建议使用线程ID作为value)
* @param timeout 超时时间(单位:S)
* @return [true: 加锁成功; false: 加锁失败]
*/
private boolean tryLock(String key, String value, Integer timeout) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(DISTRIBUTION_LOCK_KEY + key, value, timeout, TimeUnit.SECONDS);
return result != null && result.booleanValue();
}
/**
* 释放锁
*
* @param key 加锁唯一标识
* @param value 释放锁唯一标识(建议使用线程ID作为value)
*/
public void unLock(String key, String value) {
Assert.isTrue(StringUtils.isNotBlank(key), "redis locks are identified as null.");
Assert.isTrue(StringUtils.isNotBlank(value), "the redis release lock is identified as null.");
key = DISTRIBUTION_LOCK_KEY + key;
// ----- 通过value判断是否是该锁:是则释放;不是则不释放,避免误删
if (value.equals(redisTemplate.opsForValue().get(key))) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}
/**
* 注销
*/
@PreDestroy
public void destory() {
LOG.info("---redis分布式锁助手注销----");
}
}
3.配置文件
代码如下(示例):
server:
port: 8088
spring:
#redis配置
redis:
host: 192.168.6.134
port: 30511
password:
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 500
min-idle: 0
lettuce:
shutdown-timeout: 0
4.单元测试
代码如下(示例):
@Autowired
private RedisDistributionLockHelper redisDistributionLockHelper;
/**
* 测试分布式Redis锁
*/
@Test
public void testLock() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(100, 100, 20, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200));
int idx = 100;
while (--idx > 0) {
threadPoolExecutor.execute(() -> {
try {
redisDistributionLockHelper.lock("hyh", Thread.currentThread().getId() + "");
//正确结果是只有一个线程获取锁成功,才符合我们锁定资源的效果
System.out.println("---获取锁成功,当前线程ID为【" + Thread.currentThread().getId() + "】---");
} catch (RuntimeException runtimeException) {
System.out.println("---获取锁失败,当前线程ID为【" + Thread.currentThread().getId() + "】---");
}
});
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (Exception e) {
System.err.println("error:" + e.getMessage());
}
}
//线程池使用局部变量使用时记得手动关闭
threadPoolExecutor.shutdown();
}
总结
是不是感觉很简单?关注我带你揭秘更多Redis高级用法
源码地址:点此查看源码.