本文默认你已经部署了redis并成功启动,否则将无法成功运行代码。
1、maven依赖pom.xml
org.springframework.boot
spring-boot-starter-data-redis
redis.clients
jedis
2.10.2
org.projectlombok
lombok
2、application.properties
#配置Redis连接信息
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000
3、java类
RedisTestController.java
@RestController
@RequestMapping("redis")
public class RedisTestController {
private RedisTemplate redisTemplate;
@Autowired
public RedisTestController(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@GetMapping("index")
public String create() {
RedisLock redisLock = new RedisLock(redisTemplate, "test");
// boolean isGetLock = redisLock.lockRetry();
boolean isGetLock = redisLock.lockNoRetry();
try {
if (isGetLock) {
// TODO 执行业务逻辑代码
System.out.println("成功");
return "成功";
} else {
System.out.println("提交频繁,请稍等!");
return "提交频繁,请稍等!";
}
}catch (Exception e){
System.out.println("出异常");
return "出异常";
}finally {
redisLock.unlock();
}
}
}
RedisConfig.java
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
Logger logger = Logger.getLogger(getClass());
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.lettuce.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.lettuce.pool.max-wait}")
private long maxWaitMillis;
@Bean
public JedisPool redisPoolFactory() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout);
logger.info("JedisPool注入成功!!");
logger.info("redis地址:" + host + ":" + port);
return jedisPool;
}
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
logger.info("RedisTemplate注入成功!!");
return template;
}
}
RedisLock.java
@Data
public class RedisLock {
private RedisTemplate redisTemplate;
/**
* 重试时间
*/
private static final int DEFAULT_ACQUIRY_RETRY_MILLIS = 100;
/**
* 锁的后缀
*/
private static final String LOCK_SUFFIX = "_redis_lock";
/**
* 锁的key
*/
private String lockKey;
/**
* 锁超时时间,防止线程在入锁以后,防止阻塞后面的线程无法获取锁
*/
private int expireMsecs = 60 * 1000;
/**
* 线程获取锁的等待时间
*/
private int timeoutMsecs = 10 * 1000;
/**
* 是否锁定标志
*/
private volatile boolean locked = false;
/**
* 构造器
*
* @param redisTemplate
* @param lockKey 锁的key
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey) {
this.redisTemplate = redisTemplate;
this.lockKey = lockKey + LOCK_SUFFIX;
}
/**
* 构造器
*
* @param redisTemplate
* @param lockKey 锁的key
* @param timeoutMsecs 获取锁的超时时间
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs) {
this(redisTemplate, lockKey);
this.timeoutMsecs = timeoutMsecs;
}
/**
* 构造器
*
* @param redisTemplate
* @param lockKey 锁的key
* @param timeoutMsecs 获取锁的超时时间
* @param expireMsecs 锁的有效期
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs) {
this(redisTemplate, lockKey, timeoutMsecs);
this.expireMsecs = expireMsecs;
}
public String getLockKey() {
return lockKey;
}
/**
* 封装和jedis方法
*
* @param key
* @return
*/
private String get(final String key) {
Object obj = redisTemplate.opsForValue().get(key);
return obj != null ? obj.toString() : null;
}
/**
* 封装和jedis方法
*
* @param key
* @param value
* @return
*/
private boolean setNX(final String key, final String value) {
return redisTemplate.opsForValue().setIfAbsent(key, value);
}
/**
* 封装和jedis方法
*
* @param key
* @param value
* @return
*/
private String getSet(final String key, final String value) {
Object obj = redisTemplate.opsForValue().getAndSet(key, value);
return obj != null ? (String) obj : null;
}
/**
* 获取锁,获取失败的话过100ms重试,总超时时间 10 * 1000 ms
*
* @return 获取锁成功返回ture,超时返回false
* @throws InterruptedException
*/
public synchronized boolean lockRetry() {
int timeout = timeoutMsecs;
while (timeout >= 0) {
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); // 锁到期时间
if (this.setNX(lockKey, expiresStr)) {
locked = true;
return true;
}
// redis里key的时间
String currentValue = this.get(lockKey);
// 判断锁是否已经过期,过期则重新设置并获取
if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
// 设置锁并返回旧值
String oldValue = this.getSet(lockKey, expiresStr);
// 比较锁的时间,如果不一致则可能是其他锁已经修改了值并获取
if (oldValue != null && oldValue.equals(currentValue)) {
locked = true;
return true;
}
}
timeout -= DEFAULT_ACQUIRY_RETRY_MILLIS;
// 延时
try {
Thread.sleep(DEFAULT_ACQUIRY_RETRY_MILLIS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 获取锁,不重试,只获取一次,获取不到就返回失败
*
* @return 获取锁成功返回ture,失败返回false
* @throws InterruptedException
*/
public synchronized boolean lockNoRetry() {
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); // 锁到期时间
if (this.setNX(lockKey, expiresStr)) {
locked = true;
return true;
}
// redis里key的时间
String currentValue = this.get(lockKey);
// 判断锁是否已经过期,过期则重新设置并获取
if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
// 设置锁并返回旧值
String oldValue = this.getSet(lockKey, expiresStr);
// 比较锁的时间,如果不一致则可能是其他锁已经修改了值并获取
if (oldValue != null && oldValue.equals(currentValue)) {
locked = true;
return true;
}
}
return false;
}
/**
* 释放获取到的锁
*/
public synchronized void unlock() {
if (locked) {
redisTemplate.delete(lockKey);
locked = false;
}
}
}
主要方法:
redisLock.lockRetry()
获取锁,获取失败的话过100ms重试,总超时时间 10 * 1000 ms
redisLock.lockNoRetry()
获取锁,不重试,只获取一次,获取不到就返回失败
相关文章:
1、springboot 使用 RedisTemplate 整合 Redis 实现并发锁以及redis工具类
2、SSH项目集成redis(缓存示例)
PS:
如果博文有写的不对或者有更好的方式,欢迎大家评论或者私信指正。