项目
博客
归档
资源链接
关于我
项目
博客
归档
资源链接
关于我
48| 项目中的ThreadLocal、事务、锁问题讨论与解决
2024-09-21
·
·
原创
·
·
本文共 239个字,预计阅读需要 1分钟。
### 常见的几个坑之ThreadLocal和锁粒度 * ThreadLoacl的问题 * set之后建议remove ```java @Slf4j @Component public class LoginInterceptor implements HandlerInterceptor { public static ThreadLocal
threadLocal = new ThreadLocal<>(); @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { threadLocal.remove(); } } ``` * 优惠券锁粒度 * "lock:coupon:"+couponId+userid,锁粒度更细化。如果不加userId,当多个人添加优惠券会被锁住。 ```java public JsonData addCoupon(Long couponId, CouponCategoryEnum category) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); String lockKey = "lock:coupon:" + couponId+":"+loginUser.getId(); } ``` * mq监听加锁的逻辑bug * 口误,应该是先加锁,然后操作业务逻辑 ```java @RabbitHandler public void releaseCouponRecord(CouponRecordMessage recordMessage, Message message, Channel channel) throws IOException { //防止同个解锁任务并发进入;如果是串行消费不用加锁;加锁有利也有弊,看项目业务逻辑而定 Lock lock = redissonClient.getLock("lock:coupon_record_release:"+recordMessage.getTaskId()); lock.lock(); log.info("监听到消息:releaseCouponRecord消息内容:{}", recordMessage); // ... finally { lock.unlock(); } } ``` ### 常见的几个坑之事务和锁的先后顺序 * 问题: * 分布式锁放在事务内部,并不能解决超领问题,锁释放后,事务没有及时的提交,还是会超领 * 模拟方式:释放锁后-线程睡眠 ```java public JsonData addCoupon(Long couponId, CouponCategoryEnum category) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); String lockKey = "lock:coupon:" + couponId+":"+loginUser.getId(); RLock rLock = redissonClient.getLock(lockKey); //多个线程进入,会阻塞等待释放锁 rLock.lock(); log.info("领劵接口加锁成功:{}", Thread.currentThread().getId()); try { // ... } finally { rLock.unlock(); log.info("解锁成功"); } // 线程睡眠 try { TimeUnit.SECONDS.sleep(10L); } catch (InterruptedException e) { throw new RuntimeException(e); } return JsonData.buildSuccess(); } ``` * 解决方式 * 将加锁的的操作放在事务的外层,保证事务提交成功后,才能进行锁的释放 ```java public JsonData addPromotionCoupon(@ApiParam(value = "优惠券id", required = true) @PathVariable("coupon_id") long couponId) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); String lockKey = "lock:coupon:" + couponId+":"+loginUser.getId(); RLock rLock = redissonClient.getLock(lockKey); //多个线程进入,会阻塞等待释放锁 rLock.lock(); try { return couponService.addCoupon(couponId, CouponCategoryEnum.NEW_USER) } finally { rLock.unlock(); log.info("解锁成功"); } return null; } ```