系统的不断扩大,分布式锁是最基本的保障。与单机的多线程不一样的是,分布式跨多个机器。线程的共享变量无法跨机器。
为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制。但是这仅仅对单机环境有效。我们实现分布式锁大概通过三种方式。
原理剖析
====
实现
==
编写注解
/**
* @author 张新华
* @version V1.0
* @Package com.ay.framework.order.redis.product
* @date 2020年03月26日, 0026 10:29
* @Copyright © 2020 安元科技有限公司
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface StockLock {
/**
* @author zxhtom
* @Description 锁key的前缀
* @Date 15:25 2020年03月25日, 0025
* @Param []
* @return java.lang.String
*/
String prefix() default "";
/**
* @author zxhtom
* @Description key的分隔符
* @Date 15:27 2020年03月25日, 0025
* @Param []
* @return java.lang.String
*/
String delimiter() default ":";
}
/**
* @author 张新华
* @version V1.0
* @Package com.ay.framework.order.redis.product
* @date 2020年03月26日, 0026 11:09
* @Copyright © 2020 安元科技有限公司
*/
@Target({ElementType.PARAMETER , ElementType.METHOD , ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface StockParam {
/**
* @author zxhtom
* @Description 组成key
* @Date 11:11 2020年03月26日, 0026
* @Param []
* @return java.lang.String[]
*/
String[] names() default {""};
}
拦截器拦截
@Around("execution(public * *(..)) && @annotation(com.ay.framework.order.redis.product.StockLock)")
RedisAtomicLong entityIdCounter = new RedisAtomicLong(lockKey, redisTemplate.getConnectionFactory());
if (redisTemplate.hasKey(CoreConstants.UPDATEPRODUCTREDISLOCKKEY + lockKey)) {
//表示lockKey的库存信息有变动。此时无法进行交易
throw new BusinessException("库存变动。暂无法交易");
}
Long increment = entityIdCounter.decrementAndGet();
if (increment >= 0) {
try {
Object proceed = pjp.proceed();
} catch (Throwable throwable) {
//所占资源需要释放回资源池
while (!redisLock.tryGetLock(CoreConstants.UPDATEPRODUCTREDISLOCKKEY + lockKey, "")) {
}
//表示lockKey的库存信息有变动。此时无法进行交易
long l = entityIdCounter.incrementAndGet();
if (l < 1) {
redisTemplate.opsForValue().set(lockKey,1);
}
redisLock.unLock(CoreConstants.UPDATEPRODUCTREDISLOCKKEY + lockKey);
throwable.printStackTrace();
}
} else {
redisTemplate.opsForValue().set(lockKey,0);
throw new BusinessException("库存不足!无法操作");
}
上述提及工具
public Boolean tryGetLock(String key , String value) {
return tryGetLock(key, value, -1, TimeUnit.DAYS);
}
public Boolean tryGetLock(String key , String value, Integer expire) {
return tryGetLock(key, value, expire, TimeUnit.SECONDS);
}
public Boolean tryGetLock(String key , String value, Integer expire , TimeUnit timeUnit) {
ValueOperations operations = redisTemplate.opsForValue();
if (operations.setIfAbsent(key, value)) {
//说明 redis没有该key , 换言之 加锁成功 设置过期时间防止死锁
if (expire > 0) {
redisTemplate.expire(key, expire, timeUnit);
}
return true;
}
return false;
}
public Boolean unLock(String key) {
return redisTemplate.delete(key);
}
@Component()
@Primary
public class StockKeyGenerator implements CacheKeyGenerator {
@Override
public String getLockKey(ProceedingJoinPoint pjp) {
//获取方法签名
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
//获取方法cacheLock注解
StockLock stockLock = method.getAnnotation(StockLock.class);
//获取方法参数
Object[] args = pjp.getArgs();
Parameter[] parameters = method.getParameters();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < parameters.length; i++) {
StockParam stockParam = parameters[i].getAnnotation(StockParam.class);
Object arg = args[i];
if (arg instanceof Map) {
Map<String, Object> temArgMap = (Map<String, Object>) arg;
String[] names = stockParam.names();
for (String name : names) {
if (builder.length() > 0) {
builder.append(stockLock.delimiter());
}
builder.append(temArgMap.get(name));
}
}
}
return builder.toString();
}
}
问题分析
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。