分布式锁:锁了,就只有锁定的线程才能操作。
与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); }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。