• Java正常加锁但是没有起作用的问题(纪实)


    概述

      Redisson分布式锁,加锁代码完全正确,但是却没有起到锁的作用。

      首先说明一下,这里的加锁代码是正确的,不存在因为加锁错误,锁失效的问题。那么锁是正常有效的,为什么没有起到锁的作用呢。下面先说一下我的业务场景:

      这里的问题存在于支付场景,用户支付完成之后,为了确保安全快速的修改用户的订单数据,一个是第三方支付公司的回调,另外一个方面就是通过定时任务主动的轮询进行订单的查询。两条线同时确保数据能够安全快速的获取到用户的支付数据,这个时候就需要使用到锁了,那么这个锁是用在什么地方?

      两条线确保数据快速安全的,但是修改数据的只能是一条线,否则容易导致重复修改操作订单数据,导致订单数据的错误,所以这个时候要上锁,当任意一条线先修改订单之前,要上锁锁住订单,那么其他线暂时获取不到锁,锁里面先查询订单是否处于支付中的状态,如果是那么修改为支付成功,之后释放锁;这个时候另外一条线就可以获取到锁了,进去判断支付状太已经是支付成功直接退出。

    问题描述

      运行过程中,总是出现因为重复修改数据导致的错误,添加日志发现,每次进去锁之后查询出来的数据都是支付中的状态(即使确定前一条线已经修改为支付成功,但是这条线依旧查到支付中),然后两条线的执行时间间隔很短,但是能够确定的是锁是没有问题的,锁的执行确定是在上一个订单锁释放之后才执行的。那么原因到底是什么呢?

    问题的解决

      问题的解决是经过几天时间的监测以及猜测才找到,猜测过程后面说,这里先说明一下我这里遇到的情况。

    • 首先便是事物导致的。 在定时任务中执行任务的流程比较多,所以添加了事物,而且是定时任务上的事物。因为定时任务是查询一堆支付中的订单,然后循环每一个订单进行查询(在每个订单上分别加锁),如果事物存在,也就是需要所有的订单都循环完毕才能将所有的数据一起提交完成,所以在整个循环完毕之前很多订单的锁已经被释放了,但是数据未被提交,回调任务获取到的是未被提交的数据,这个时候就导致了重复修改数据。修改方案就是将单个订单进行事物操作便可。
    • 之后便是缓冲导致的。 项目中使用了mybatis进行数据库的连接查询,我们都应该知道mybatis是存在一级缓冲和二级缓冲的,默认情况下一级缓冲是开启的,也就是说当你一个线程查询数据之后再次以同样的方式进行查询的时候,是直接从一级缓冲中提取数据的。正好在回调方法中上锁之前进行了一次订单查询,上锁之后又用同样的条件进行了数据的查询,导致了数据获取到的是缓冲中的数据,而不是数据库中已经被修改的数据。修改的方案很简单,通过添加不同的查询条件,这样就不会直接从缓冲中进行数据的获取了。

    这两种情况都是比较碰巧的情况下才会出现,尤其是缓冲这个。但是当订单量足够大的时候就不是碰巧了,而是必然会出现的错误。

    猜测过程

      在解决问题的过程中除了上面两种原因还有过了一个关于错误原因的猜测,但是目前无法进行测试证明,仅仅是一个猜测。

      首先便是redisson分布式锁是一种可重入锁,也就说一个如果是一个线程,可以重复进入这个锁。所以个人猜测有没有可能执行定时任务和回调方法的是同一个线程。因为定时任务和回调方法都是从线程池中获取线程的,虽然说线程被激活获取后,不在闲置队列,无法被再次获取,但是有没有可能县城在执行过程中停个几毫秒(类似sleep),从而被其他方法获取执行,导致执行两个方法的是同一个线程。

      这个只是个人的一种猜测,请了解的兄弟指点。

  • 相关阅读:
    uniapp缓存对象数组
    webstorm HbuilderX工具未配置
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task
    配置.ssh功能
    全国机动车达4.3亿辆 驾驶人达5.2亿人 新能源汽车保有量达1821万辆
    4. react路由
    为何面试官总是将你简历上的技术问题问到回答不出来为止?
    Android 12 init(1) 启动流程分析
    16、注册中心-consul
    大一作业HTML网页作业 HTML校园篮球网页作业(12个页面)
  • 原文地址:https://blog.csdn.net/Lou_Lan/article/details/132747445