redis 实现 分布式锁,排队等待取得锁
Posted a393060727
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis 实现 分布式锁,排队等待取得锁相关的知识,希望对你有一定的参考价值。
分布式锁:锁了,就只有锁定的线程才能操作。
与java中的锁类似,只是我们是否锁定是依托与第三方redis中的一个key标识判断是否可以操作。
现在场景是:一个订单来了,必须处理,等待上个线程处理完后,竞争取得锁,否则就处理超时,业务处理失败。
下面是锁的工具类:
很奇怪的是,取不到锁时,等待期间不能休眠,否则还是锁不住。。所以就不休眠,作死了遍历查询。
import java.util.Arrays; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import lombok.extern.slf4j.Slf4j; /** * @Desc : 分布式锁 * @Company : 晨创科技 * @author : Chenweixian 陈惟鲜 * @Date : 2018年6月5日 下午2:41:03 */ @Slf4j public class RedisLockUtil { private static final Long RELEASE_SUCCESS = 1L; private static final StringRedisTemplate stringRedisTemplate = (StringRedisTemplate) SpringUtils.getBean("stringRedisTemplate"); private static final DefaultRedisScript<Long> redisScript = (DefaultRedisScript<Long>) SpringUtils.getBean("redisScript"); private static long timeout = 65 * 1000L; // 锁定65S /** * 上锁 * @param key 锁标识 * @param value 线程标识 * @return 上锁状态 */ public static boolean lock(String key, String value, long mytimeout) { long start = System.currentTimeMillis(); // 检测是否超时 if (System.currentTimeMillis() - start > mytimeout) { return false; } // 执行set命令 Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(key, value, mytimeout, TimeUnit.MILLISECONDS);// 毫秒 // 是否成功获取锁 if (absent) { return true; } return false; } /** * 上锁 * @param key 锁标识 * @param value 线程标识 * @return 上锁状态 */ public static boolean lock(String key, String value) { long start = System.currentTimeMillis(); while (true) { // 检测是否超时 if (System.currentTimeMillis() - start > timeout) { return false; } // 执行set命令 Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);// 毫秒 // 是否成功获取锁 if (absent) { return true; } // 不能休眠 // try { // Thread.sleep(5);// 等待50毫秒 // } catch (InterruptedException e) { // } } } /** * 解锁 * @param key 锁标识 * @param value 线程标识 * @return 解锁状态 */ public static boolean unlock(String key, String value) { // 使用Lua脚本:先判断是否是自己设置的锁,再执行删除 Long result = stringRedisTemplate.execute(redisScript, Arrays.asList(key, value)); // 返回最终结果 return RELEASE_SUCCESS.equals(result); } }
调用方法:
做了个注解@RedisLock,意味着,加上这个注解后,都会取锁后才能执行。
这里是针对加上注解的类方法锁定
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; /** * @Desc : 分布式锁切面拦截,方法上加上@RedisLock即可 * @author : Chenweixian 陈惟鲜 * @Date : 2018年5月7日 下午7:47:56 */ @Slf4j @Aspect @Component @Order(7) // @Order(1)数字越小优先级越高 public class RedisLockAspect { /**拦截所有controller包下的方法*/ @Pointcut("@annotation(com.ccjr.commons.annotation.RedisLock)") private void lockMethod(){ } /** * 日志打印 * @author : 陈惟鲜 chenweixian * @Date : 2018年8月8日 下午5:29:47 * @param point * @return * @throws Throwable */ @Around("lockMethod() && @annotation(redisLock)") public Object doAround(ProceedingJoinPoint point, RedisLock redisLock) throws Throwable { String class_name = point.getTarget().getClass().getName(); String method_name = point.getSignature().getName(); String threadUUID = UUIDUtils.generateUUID64(); String redisKey = RedisConstants.keyRedisLock(class_name + "." + method_name); Object result = null; try { RedisLockUtil.lock(redisKey, threadUUID); log.info("lock aop rediskey :"+redisKey); result = point.proceed();// result的值就是被拦截方法的返回值 } finally { RedisLockUtil.unlock(redisKey, threadUUID); log.info("unlock aop rediskey :"+redisKey); } return result; } }
注解类RedisLock.java
/** * @Desc : redisLock注解标签,使用该标签后,并且在app中配置RedisLockAop后,项目将实现注解分布式锁, * key为当前类+方法名 * @author : Chenweixian 陈惟鲜 * @Date : 2018年5月7日 下午7:44:56 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RedisLock { }
调用时就简单了:
@Override @RedisLock public BaseResponse<String> genHisTask(BaseRequest<GenHisAssetsAnalyzeTaskReq> baseRequest) { String msg = "生成历史数据"; // 业务处理过程。。。。。。return baseApiService.setResultSuccess(); }
当然如果针对某块来锁定的,比如锁定某个客户的库存,
客户买入时冻结钱,商家卖出时冻结商品,针对一个商品号goodsId锁定
String threadUUID = UUIDUtils.generateUUID64(); String redisKey = RedisConstants.keyRedisLockCash(goodsId); try { if (!RedisLockUtil.lock(redisKey, threadUUID)) { throw new BusinessBizException(ApiErrorEnum.TRADE_CASH_21000, goodsId); } // 执行业务过程。。。。。。 results.add(result); } finally { RedisLockUtil.unlock(redisKey, threadUUID); }
以上是关于redis 实现 分布式锁,排队等待取得锁的主要内容,如果未能解决你的问题,请参考以下文章