JAVA语言之redis 分布式锁
小标 2019-03-04 来源 : 阅读 602 评论 0

摘要:本文主要向大家介绍了JAVA语言之redis 分布式锁,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言之redis 分布式锁,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

JAVA语言之redis 分布式锁

最近抽空优化了之前已有的redis分布式锁,主要用于解决高并发的问题,比如抢红包,多个人同时操作红包库存,当在库存只剩下1个的时候,一个人的减库存的操作事务没提交,另一个人的查库存操作刚好同步执行,这样就会出现很尴尬的事情,1个红包会被2个人抢走,这个时候,我们就要依托锁,将请求入口锁住,当然锁有很多种方式,这边就记录一下比较好用的redis分布式锁。


方式有很多setNX 、set、incr等等,setNX只要通过逻辑防止死锁就可以了


直接上代码:


public boolean keyLock(final String key, final long keepMin) {
boolean obj = false;
try {
obj = (boolean) redisTemplateSerializable.execute(new RedisCallback<Object>() {


@Override
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
try{
Long incr = connection.incr(key.getBytes());
if(incr == 1){
connection.setEx(key.getBytes(), keepMin, incr.toString().getBytes());
return true;
}else{
Long ttl = connection.ttl(key.getBytes());
if(ttl == -1){
//设置失败,重新设置过期时间
connection.setEx(key.getBytes(), keepMin, incr.toString().getBytes());
return true;
}
}
}catch (Exception e) {
logger.error("加锁异常", e);
connection.del(key.getBytes());
return true;
}


return false;
}


});
}catch (Exception e) {
logger.error(e.getMessage());
}


return obj;
}
注解


package com.tp.soft.common.interceptor;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**



  • redis锁注解




  • @

  • author taopbr/>*/

  • @Retention(RetentionPolicy.RUNTIME)<br <="" a="">@Target({ ElementType.METHOD })br/>@Documented<br <="" a="">public @interface RedisLock {

  • String lockName() default ""; // 锁名

  • int retryTimes() default 0; // 重试次数

  • long retryWait() default 200; // 重试等待时间,单位 : ms

  • int keeyMinTime() default 1; //锁自动失效时间 1秒

  • }

  • aop


package com.tp.soft.aop.redis;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import cn.hutool.core.lang.Assert;
import com.tp.soft.common.interceptor.Cacheable;
import com.tp.soft.common.interceptor.RedisLock;
import com.tp.soft.redis.RedisCacheSvc;br/>@Aspect<br <="" a="">@Component
public class RedisLockAop {
private static final Logger log = LoggerFactory.getLogger(RedisLockAop.class);


private static final String LOCK_NAME = "lockName";
private static final String RETRY_TIMES = "retryTimes";
private static final String RETRY_WAIT = "retryWait";
private static final String KEEP_MIN_TIME = "keepMinTime";br/>@Resource<br <="" a="">private RedisCacheSvc redisCacheSvc;br/>@Pointcut("@annotation(com.tp.soft.common.interceptor.RedisLock)")<br <="" a="">public void redisLockAspect() {br/>}
@Around("redisLockAspect()")<br <="" a="">public Object lockAroundAction(ProceedingJoinPoint pjp) throws Throwable {
Method method = returnMethod(pjp);


Map<String, Object> annotationArgs = this.getAnnotationArgs(pjp);
String lockPrefix = (String) annotationArgs.get(LOCK_NAME);
Assert.notNull(lockPrefix, "分布式,锁名不能为空");


int retryTimes = (int) annotationArgs.get(RETRY_TIMES);
long retryWait = (long) annotationArgs.get(RETRY_WAIT);
int keepMinTime = (int) annotationArgs.get(KEEP_MIN_TIME);
String keyName = parseKey(lockPrefix, method, pjp.getArgs());


// 获取redis锁,防止死锁
boolean keyLock = redisCacheSvc.keyLock(keyName, keepMinTime);
if(keyLock){
//执行主程序
return pjp.proceed();
}else{
if(retryTimes <= 0){
log.info(String.format("{%s}已经被锁, 不重试", keyName));
throw new RuntimeException(String.format("{%s}已经被锁, 不重试", keyName));
}


int failCount = 1;
while (failCount <= retryTimes) {
// 等待指定时间ms
try {
Thread.sleep(retryWait);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (redisCacheSvc.keyLock(keyName, keepMinTime)) {
// 执行主逻辑
return pjp.proceed();
} else {
log.info(String.format("{%s}已经被锁, 正在重试[ %s/%s ],重试间隔{%s}毫秒", keyName, failCount, retryTimes, retryWait));
failCount++;
}
}


throw new RuntimeException("系统繁忙, 请稍等再试");
}


}
/**



获取锁参数




@param proceeding


@return
*/
private Map<String, Object> getAnnotationArgs(ProceedingJoinPoint proceeding) {
Class target = proceeding.getTarget().getClass();
Method[] methods = target.getMethods();
String methodName = proceeding.getSignature().getName();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Map<String, Object> result = new HashMap<String, Object>();
RedisLock redisLock = method.getAnnotation(RedisLock.class);
result.put(LOCK_NAME, redisLock.lockName());
result.put(RETRY_TIMES, redisLock.retryTimes());
result.put(RETRY_WAIT, redisLock.retryWait());
result.put(KEEP_MIN_TIME, redisLock.keeyMinTime());
return result;
}
}
return null;
}
private Method returnMethod(ProceedingJoinPoint pjp)
throws NoSuchMethodException {
Signature signature = pjp.getSignature();
Class<? extends Object> cls = pjp.getTarget().getClass();
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
Method method = cls.getDeclaredMethod(signature.getName(),
targetMethod.getParameterTypes());
return method;
}
/**


获取缓存的key key 定义在注解上,支持SPEL表达式




@param pjp


@return
*/
private String parseKey(String key, Method method, Object[] args) {
// 获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paraNameArr = u.getParameterNames(method);
// 使用SPEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
// SPEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
// 把方法参数放入SPEL上下文中
for (int i = 0; i < paraNameArr.length; i++) {
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(key).getValue(context, String.class);
}
}
搭建完成后直接在需要锁住的接口上注解



@RedisLock(lockName="'lock_'+#tbbId",retryTimes=5)
模拟高并发测试


for (int i = 0; i < 2; i++) {
threadPoolTaskExecutor.execute(new StartTaskThread(redisCacheSvc, i, threadPoolTaskExecutor));
}
redis 分布式锁
效果就是这样了


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程