本次基于注解+AOP实现分布式锁(招式与前文基于注解切换多数据源相同),话不多说,直接上样例:

首先自定义注解:设计时需要考虑锁的一般属性:keys,最大等待时间,超时时间,时间单位。

package com.paic.phssp.springtest.redisson;

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; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequestLockable {
String[] key() default ""; long maximumWaiteTime() default 2000; long expirationTime() default 1000; TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

新建一个抽象请求拦截器,设计模式:装饰模式,父类决定整体流程,具体细节交给字类实现,便于解耦扩展。

package com.paic.phssp.springtest.redisson;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors; public abstract class AbstractRequestLockInterceptor {
protected abstract Lock getLock(String key); protected abstract boolean tryLock(long waitTime, long leaseTime, TimeUnit unit, Lock lock) throws InterruptedException; private static final String[] removeSigs = new String[]{"#","{","}"}; @Around("@annotation(RequestLockable)")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
//获取连接点的方法签名对象
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod();
String methodName = signature.getName(); //获取连接点所在的目标对象
String targetName = point.getTarget().getClass().getName(); //获取连接点方法运行时的入参列表
Object[] arguments = point.getArgs(); if (method != null && method.isAnnotationPresent(RequestLockable.class)) {
RequestLockable requestLockable = method.getAnnotation(RequestLockable.class); String requestLockKey = getLockBySpellKey(method, targetName, methodName, requestLockable.key(), arguments); System.out.println(">>>>requestLockKey="+requestLockKey); Lock lock = this.getLock(requestLockKey); boolean isLock = this.tryLock(requestLockable.maximumWaiteTime(), requestLockable.expirationTime(),
requestLockable.timeUnit(), lock);
if (isLock) {
try {
return point.proceed();
} finally {
//释放锁资源
lock.unlock();
}
} else {
throw new RuntimeException("获取锁资源失败");
}
} //通过反射执行目标对象的连接点处的方法
return point.proceed();
} /**
* 组装lock key
*
* @param method
* @param targetName 对象名
* @param methodName 方法名
* @param keys 注解key
* @param arguments 方法参数
* @return
*/
private String getLockBySpellKey(Method method, String targetName, String methodName, String[] keys, Object[] arguments) {
StringBuilder lockKey = new StringBuilder();
lockKey.append("lock.").append(targetName).append(".").append(methodName); if (keys != null) {
//Joiner Guava包
//String keyStr = Joiner.on(".").skipNulls().join(keys);
String keyStr = Arrays.stream(keys).filter(Objects::nonNull).collect(Collectors.joining("."));
System.out.println("RequestLockable:keys="+keyStr); if (!StringUtils.isBlank(keyStr)) {
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameters = discoverer.getParameterNames(method); //用Spell方法容易出问题,所以这里加了一些限制
keyStr = creatSpellExpressionStr(keyStr,parameters,removeSigs); int length = parameters.length;
if(length > 0){
if(!hasParameters(keyStr,parameters)){
//不包含参数直接用keyStr
lockKey.append("#").append(keyStr);
}else{
//keyStr 是否包含参数名,如果包含可用Spell表达式
//用Spell方法容易出问题,所以这里加工下
keyStr = creatSpellExpressionStr(keyStr,parameters,removeSigs);
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
Map<String,Object> vMap = new HashMap<String,Object>();
for (int i = 0; i < length; i++) {
//key:方法参数名,val:值
vMap.put(parameters[i],arguments[i]);
}
((StandardEvaluationContext) context).setVariables(vMap); //eg:#{#proKey}.asd#{#proKey}fa.#{#proId}#{#proKey} -> product.asdproductfa.123product
Expression expression = parser.parseExpression(keyStr,new TemplateParserContext());
String keysValue = expression.getValue(context, String.class);
lockKey.append("#").append(keysValue);
}
}
}
}
return lockKey.toString();
} private boolean hasParameters(String lockKey,String[] parameters){
boolean hasFlag = false;
for(String str : parameters){
if(StringUtils.indexOf(lockKey,str) != -1){
hasFlag = true;
break;
}
}
return hasFlag;
} private String creatSpellExpressionStr(String lockKey,String[] parameters,String[] removeSigs){
//去掉#{}等字符
for(String sig : removeSigs){
lockKey = StringUtils.replace(lockKey,sig,"");
} for(String str : parameters){
String repStr = "#{#"+str+"}";
lockKey = StringUtils.replace(lockKey,str,repStr);
}
return lockKey;
} }

具体实现拦截类:

package com.paic.phssp.springtest.redisson;

import org.aspectj.lang.annotation.Aspect;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.stereotype.Component; import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock; @Aspect
@Component
public class RedisRequestLockInterceptor extends AbstractRequestLockInterceptor {
@Resource
private Redisson redisson; @Override
protected Lock getLock(String key) {
return redisson.getLock(key);
} @Override
protected boolean tryLock(long waitTime, long leaseTime, TimeUnit unit, Lock lock) throws InterruptedException {
return ((RLock) lock).tryLock(waitTime, leaseTime, unit);
}
}
ProductService.java
 /**
* 注意:key={"#proKey","#proId"} 与参数名一致时走Spell表达式
* @param proKey
* @param proId
*/
@RequestLockable(key={"#{#proKey}","#{#proId}"}) //细化到参数,参数值不同时,不共享一个锁,相同时才会共享,这点要注意
//@RequestLockable(key={"#lock_key_prod"})
public void anotationProd(String proKey,int proId){
String productId = stringRedisTemplate.opsForValue().get(proKey);
int sprodId = Integer.parseInt(productId);
if (sprodId > 0) {
stringRedisTemplate.opsForValue().set("product", --sprodId + "");
System.out.println(">>>>>>"+Thread.currentThread().getName() + ",product:" + sprodId + "");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

单元测试(比较懒,就不另外起工程测了,开多线程...)

@Test
public void testAnotationProd(){
//开启2个线程
Thread thread1 = new Thread(()-> productService.anotationProd("product",1));
Thread thread2 = new Thread(()-> productService.anotationProd("product",1)); thread1.start();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start(); try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

场景1:方法加注释:@RequestLockable(key={"#{#proKey}","#{#proId}"})

RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.1
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.1
>>>>>>Thread-10,product:92
>>>>>>Thread-9,product:91

场景2:方法加注释:@RequestLockable(key={"#lock_key_prod"})
RequestLockable:keys=#lock_key_prod
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#lock_key_prod
RequestLockable:keys=#lock_key_prod
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#lock_key_prod
>>>>>>Thread-9,product:90
>>>>>>Thread-10,product:89

场景3:方法不加注释,此时两个线程同时去读写了
>>>>>>Thread-9,product:88
>>>>>>Thread-10,product:88

场景4:测试另一个线程,参数proId=2,发现两个线程同时去读写了
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.1
RequestLockable:keys=#{#proKey}.#{#proId}
>>>>requestLockKey=lock.com.paic.phssp.springtest.redisson.ProductService.anotationProd#product.2
>>>>>>Thread-9,product:87
>>>>>>Thread-10,product:87

参考:

https://blog.csdn.net/qq_15427331/article/details/54630999

Redisson实现分布式锁(二)的更多相关文章

  1. Redisson实现分布式锁(3)—项目落地实现

    Redisson实现分布式锁(3)-项目落地实现 有关Redisson实现分布式锁前面写了两篇博客作为该项目落地的铺垫. 1.Redisson实现分布式锁(1)---原理 2.Redisson实现分布 ...

  2. Redisson实现分布式锁(2)—RedissonLock

    Redisson实现分布式锁(2)-RedissonLock 有关Redisson实现分布式锁上一篇博客讲了分布式的锁原理:Redisson实现分布式锁---原理 这篇主要讲RedissonLock和 ...

  3. Redisson实现分布式锁(1)---原理

    Redisson实现分布式锁(1)---原理 有关Redisson作为实现分布式锁,总的分3大模块来讲. 1.Redisson实现分布式锁原理 2.Redisson实现分布式锁的源码解析 3.Redi ...

  4. spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)

    一,为什么要使用分布式锁? 如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性 但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁, ...

  5. 基于Redisson实现分布式锁源码解读

    文章目录 一.分布式锁的概念 和 使用场景 二.将redis官网对于分布式锁(红锁)的定义和Redisson实现做概括性总结 三.基于Redisson的分布式实现方案 四.加锁过程分析 五.锁重入过程 ...

  6. redisson之分布式锁实现原理(三)

    官网:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 一.什么是分布式锁 1.1.什么是分布式锁 分布式锁,即分布式系统中的锁 ...

  7. Redisson实现分布式锁

    转: Redisson实现分布式锁 Redisson文档参考:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 redis是实现 ...

  8. 使用Redisson实现分布式锁,Spring AOP简化之

    源码 Redisson概述 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).它不仅提供了一系列的分布式的Java常用对象,还提供了许多 ...

  9. 利用Redisson实现分布式锁及其底层原理解析

    Redis介绍 参考地址:https://blog.csdn.net/turbo_zone/article/details/83422215 redis是一个key-value存储系统.和Memcac ...

  10. 【高并发】你知道吗?大家都在使用Redisson实现分布式锁了!!

    写在前面 忘记之前在哪个群里有朋友在问:有出分布式锁的文章吗-@冰河?我的回答是:这周会有,也是[高并发]专题的.想了想,还是先发一个如何使用Redisson实现分布式锁的文章吧?为啥?因为使用Red ...

随机推荐

  1. ARM 架构、ARM7、ARM9、STM32、Cortex M3 M4 、51、AVR 之间有什么区别和联系?(转载自知乎)

    ARM架构:  由英国ARM公司设计的一系列32位的RISC微处理器架构总称,现有ARMv1~ARMv8种类. ARM7:       一类采用ARMv3或ARMv4架构的,使用冯诺依曼结构的内核. ...

  2. yolo3(目标检测)实测

    yolo是继faster-r-cnn后,原作者在目标检测领域进行的新研究.到了v3版本以后,虽然已经换人支持,但是更注重工程实践,在实际使用过程中突出感受就是 “非常快”,GPU加速以后能够达到实时多 ...

  3. 20145308 《网络对抗》 MSF基础应用 学习总结

    20145308 <网络对抗> MSF基础应用 学习总结 实验内容 掌握metasploit的基本应用方式,掌握常用的三种攻击方式的思路.具体需要完成(1)一个主动攻击,如ms08_067 ...

  4. Codeforces 903G Yet Another Maxflow Problem - 线段树

    题目传送门 传送门I 传送门II 传送门III 题目大意 给定一个网络.网络分为$A$,$B$两个部分,每边各有$n$个点.对于$A_{i} \ (1\leqslant i < n)$会向$A_ ...

  5. Codeforces 40E Number Table - 组合数学

    题目传送门 传送门I 传送门II 题目大意 给定一个$n\times m$的网格,每个格子上要么填$1$,要么填$-1$,有$k$个位置上的数是已经填好的,其他位置都是空的.问有多少种填法使得任意一行 ...

  6. topcoder srm 445 div1

    problem1 link 这个的结论是只需要考虑坐标是整数或者是整数.5,比如(2.5,3),(4,3.5),(1.5,4.5)这样的时候.这个详细证明起来应该挺麻烦的.这里有一些讨论. probl ...

  7. IPVS负载均衡

    概念: ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4层LAN交换,作为 Linux 内核的一部分.ipvs运行在主机上,在真实服务器集群前充当负载均衡器. ...

  8. HihoCoder 1634 Puzzle Game(最大子矩阵和)题解

    题意:给一个n*m的矩阵,你只能选择一个格子把这个格子的数换成p(也可以一个都不换),问最大子矩阵和最小可能是多少? 思路: 思路就是上面这个思路,这里简单讲一下怎么n^3求最大子矩阵和:枚举两行(或 ...

  9. .Net Core 全球化&本地化的使用

    官网文档 nuget地址 创建资源文件 添加资源文件 实施策略 配置本地化 本地化中间件 使用 视图本地化 DataAnnotations 本地化 Make the app's content loc ...

  10. POJ 3278 Catch That Cow(赶牛行动)

    POJ 3278 Catch That Cow(赶牛行动) Time Limit: 1000MS    Memory Limit: 65536K Description - 题目描述 Farmer J ...