JAVA RedisTemplate实现(加锁/解锁) 解决高并发问题
基于传统的单机模式下的并发锁,已远远不能满足当下高并发大负载的情况,当下常用的并发处理如下1、使用synchronized关键字2、selectfor update 乐观锁3、使用redis实现同步锁方案一 适合单机模式,方案二 虽然满足多节点服务实例但 对变更操作的吞吐量有影响方案三 基于redis nosql数据库 在效率与横向扩展方面都大大优于前两种方案redis 单线程 在自身设计上一定程
基于传统的单机模式下的并发锁,已远远不能满足当下高并发大负载的情况,当下常用的并发处理如下
1、使用synchronized关键字
2、select for update 乐观锁
3、使用redis实现同步锁
方案一 适合单机模式,
方案二 虽然满足多节点服务实例但 对变更操作的吞吐量有影响
方案三 基于redis nosql数据库 在效率与横向扩展方面都大大优于前两种方案
redis 单线程 在自身设计上一定程度可避免想成不安全 再者其效率高于关系型数据库
本次实现锁机制 基于redis 的两个指令 查询 redis.cn 网站
指令一:SETNX key value
将key
设置值为value
,如果key
不存在,这种情况下等同SET命令。 当key
存在时,什么也不做。SETNX
是”SET if Not eXists”的简写。
Integer reply, 特定值:
1
如果key被设置了0
如果key没有被设置
指令二:GETSET key value
自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。
bulk-string-reply: 返回之前的旧值,如果之前Key
不存在将返回nil
。
步骤一, pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
步骤二,RedisCacheAutoConfiguration
package com.wh.whcloud.df.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wh.whcloud.core.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis缓存配置
*
* @author
*/
@Slf4j
@Configuration
@AutoConfigureAfter({RedisAutoConfiguration.class})
public class RedisCacheAutoConfiguration {
/**
* 重新配置一个RedisTemplate
*
* @param factory
* @return
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setDefaultSerializer(jackson2JsonRedisSerializer);
log.info("RedisTemplate配置 [{}]", template);
return template;
}
@Bean
@ConditionalOnMissingBean(RedisUtil.class)
@ConditionalOnBean(RedisTemplate.class)
public RedisUtil redisUtils(RedisTemplate redisTemplate) {
RedisUtil redisUtil = new RedisUtil(redisTemplate);
log.info("RedisUtil [{}]", redisUtil);
return redisUtil;
}
}
步骤三, RedisLock 加解锁实现
package com.wh.whcloud.util;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
public class RedisLock {
private static final Long SUCCESS = 1L;
private long timeout = 9999; //获取锁的超时时间
/**
* 加锁,无阻塞
*
* @param
* @param
* @return
*/
public static Boolean tryLock(RedisTemplate redisTemplate, String key, String value, long expireTime) {
try {
//SET命令返回OK ,则证明获取锁成功
Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
return ret;
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
/*
Long start = System.currentTimeMillis();
for(;;){
//SET命令返回OK ,则证明获取锁成功
Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
return ret;
//否则循环等待,在timeout时间内仍未获取到锁,则获取失败
long end = System.currentTimeMillis() - start;
if (end>=timeout) {
return false;
}
}*/
/**
* 解锁
*
* @param
* @param
* @return
*/
public static Boolean unlock(RedisTemplate redisTemplate, String key, String value) {
try {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
//redis脚本执行
//Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value))
Object result = redisTemplate.delete(Collections.singletonList(key));
if (SUCCESS.equals(result)) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
}
注意:
redisTemplate.execute 是redis脚本执行,可能报错, 具体参考: https://blog.csdn.net/cevery/article/details/108303919
测试:我这里自己创建两个 线程测试
@Slf4j
@Component
public class Schedule {
@Resource
private RedisTemplate redisTemplate;
@Scheduled(cron = "0 0/1 * * * ? ")
public void getDeviceStatus() throws InterruptedException {
//启动两个线程,测试哪一个能够悠闲抢到Redis锁
Test1Runnable test1 = new Test1Runnable();
new Thread(test1).start();
Test2Runnable test2 = new Test2Runnable();
new Thread(test2).start();
}
//自定义个唯一的Key值
public String key = "ZX123456789";
//保存数据方法
public boolean testSave(){
//获取锁
boolean isOK = RedisLock.tryLock(redisTemplate, key, "key+1", 2000);
//处理业务 ,然后释放锁
if(isOK){
System.out.println("处理完业务,释放锁==="+RedisLock.unlock(redisTemplate, key, "key+1"));
}
return isOK;
}
class Test1Runnable implements Runnable {
@Override
public void run() {
boolean result = testSave();
log.info(Thread.currentThread().getName()+"Test1获取锁:"+result);
}
}
class Test2Runnable implements Runnable {
@Override
public void run() {
boolean result = testSave();
log.info(Thread.currentThread().getName()+"Test2获取锁:"+result);
}
}
}
效果:
更多推荐
所有评论(0)