Java乐观锁的实现

2023-09-21 17:21:59

乐观锁是一种用于解决并发冲突的机制,它基于假设在大多数情况下没有并发冲突的原则。与悲观锁不同,乐观锁不会对数据加锁,而是通过一定的方式来检测并处理并发冲突。

在实现乐观锁时,通常会使用版本号或时间戳作为标识。当多个线程同时访问同一个数据时,每个线程都会读取到数据的当前版本号或时间戳。在更新数据时,线程会比较当前的版本号或时间戳与自己读取到的版本号或时间戳是否一致。如果一致,则执行更新操作;如果不一致,则表示有其他线程已经修改了数据,当前线程的操作可能存在并发冲突,需要进行相应的处理(如重试、回滚等)。 

乐观锁的实现有两种

  • CAS
  • 版本号控制

CAS

当多个线程尝试同时修改同一个内存位置时,先比较当前内存位置的值与期望值是否相等,如果相等,则将新值写入内存,否则不进行任何更改。在这个过程中,所有的操作都是原子的,即每次只能有一个线程执行这个操作。这样就避免了同时修改内存位置带来的竞争和冲突问题。

 private AtomicInteger value=new AtomicInteger(0);

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateTradeOrder(String tradinId, String buyerId) throws InterruptedException {

        // 获取发布详情
        TradinPost tradinPost = tradinPostMapper.selectOne(new QueryWrapper<TradinPost>()
                                                            .eq("tradin_id", tradinId));
        // 判断是否有人已经下单了
        if (tradinPost.getStatus() != 0){
            // 有人下单了
            return null;
        }

        // 乐观锁基于cas更新
        // 获取原子类值
        int current=value.get();
        int expect=current+1;
        // 已经给人抢先下单了,慢了一步返回null
        if (!value.compareAndSet(current,expect)){
            return null;
        }
        //更新商品数量
        int update = tradinPostMapper.update(null, new UpdateWrapper<TradinPost>()
                .setSql("status=status+1")
                .eq("tradin_id", tradinId));
        if (update < 1){
            throw new BusinessException("更新失败");
        }
        // 生成订单
        String orderId= StringUtil.generateShortId();
        // 。。。。。。。
        // 生成订单的业务
        // 。。。。。。。
        return orderId;

   }

版本号控制

当多个线程同时对同一数据进行更新时,每个线程在读取数据之后都会获取该数据的当前版本号。在执行更新操作时,线程会比较自己读取到的版本号与实际数据的当前版本号是否一致。如果一致,表示数据未被其他线程修改过,可以执行更新操作,并将版本号加一;如果不一致,表示数据已经被其他线程修改过,当前线程的操作可能存在并发冲突,需要进行相应的处理。

// 乐观锁基于版本号更新
// 更新版本号
int update = tradinPostMapper.update(null, new UpdateWrapper<TradinPost>()
         .setSql("status=status+1")
         .eq("tradin_id", tradinId)
         .eq("status", tradinPost.getStatus()));
if (update < 1){
          // 更新版本号失败,已经给人抢先下单了,慢了一步返回null
         return null;
}

原理:当有多个线程同时执行update只有一个线程执行成功,只有版本相同的对应的数据才能更新成功,返回1

如果需要要重试的场景,可以使用自旋调用自身重试

// 乐观锁基于版本号更新
// 更新版本号
int update = tradinPostMapper.update(null, new UpdateWrapper<TradinPost>()
                .setSql("status=status+1")
                .eq("tradin_id", tradinId)
                .eq("status", value.get()));
if (update < 1){
            // 更新版本号失败,自旋重试
            this.generateTradeOrder(tradinId,buyerId);
}

更多推荐

ctfshow web入门(2)

web11打开这个网站,到网站诊断分析模块搜索域名web12提示有时候网站上的公开信息,就是管理员常用密码打开,就是个购物网站因为昨天刚做robots.txt我就搜了一下真的有,提示admin这个页面访问一下,username肯定是admin,但是密码。有点晕。但是看到提示我就又去看了看原来的页面,有没有可疑的密码在末

MQTT服务器搭建

本次搭建的MQTT服务器是emqx提供的服务器1、下载https://www.emqx.com/en/downloads/broker从官网下载5.2.0版本emqx-5.2.0-windows-amd64.zip下载完成直接安装2、配置,修改端口号mqtt默认端口号常规的用法,我们一般使用和开放这两个端口:1883,

php生成随机验证码图片

1,CaptchaPicture.php用于生成画布,然后在画布上生成四位随机验证码<?phpsession_start();header("Content-type:image/png");//创建图像的格式$image_width=76;//设置图像的宽度$image_height=40;//设置图像的高度$len

线性代数与编程语言结合 基础

什么是线性代数线性代数是数学的一个分支,研究向量空间和线性变换的理论与方法。它涉及了向量、矩阵、线性方程组、线性映射等概念与运算规则。线性代数在科学和工程领域中被广泛应用,如物理学、计算机图形学、统计学、电子工程等。它提供了一种强大的工具和语言来描述和解决线性问题,比如矩阵求逆、解线性方程组、特征值和特征向量等。通过线

抖音矩阵系统源码:开发搭建

一、抖音矩阵系统源码开发概述抖音seo矩阵系统源码开发技术要求十分严格。首先,需要熟练掌握Python、Java等编程语言,具有扎实的算法基础。在此基础上,还需要具备深度学习、神经网络等相关技能,能够实现精准推荐和内容分析等功能。其次,抖音seo矩阵系统开发还需要专业的云计算技术支持,比如分布式计算、负载均衡等,以确保

智能配电房监控系统:实现配电智能化管理

智能配电房监控系统是一种基于现代信息技术,实现对配电房设备运行状态实时监控的智能化系统。它能够实时监测配电房设备的运行状态,及时发现设备故障,提高配电系统的可靠性,同时还可以实现远程监控和智能化控制,提高配电系统的效率。一、智能配电房监控系统的构成智能配电房监控系统主要由监控终端、通信网络和监控中心三部分构成。1.监控

ECharts

ECharts是一款基于JavaScript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。ECharts提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还

Android 12 源码分析 —— 应用层 六(StatusBar的UI创建和初始化)

Android12源码分析——应用层六(StatusBar的UI创建和初始化)在前面的文章中,我们分别介绍了Layout整体布局,以及StatusBar类的初始化.前者介绍了整体上面的布局,后者介绍了三大窗口的创建的入口处,以及需要做的准备工作.现在我们分别来细化三大窗口的UI创建和初始化,首先从StatusBar窗口

【初阶数据结构】——堆排序和TopK问题

=========================================================================个人主页代码仓库C语言专栏初阶数据结构专栏Linux专栏===========================================================

在React中使用Immutable.js

Immutable的几种数据类型在React中使用Immutable.js实例扩展LodashLodash常用api分类Lodash中常用的API介绍:Lodash中api和javascript中api有什么不同react中如何使用LodashImmutable的几种数据类型List:有序索引集,类似JavaScrip

新版发布 | Cloudpods v3.10.5 和 v3.9.13 正式发布

Cloudpodsv3.10.5本期发布中,ocboot部署脚本有较多变化,首先支持以非root用户执行安装流程,其次响应社区的呼吁,增加了–stack参数,允许Allinone一键安装仅包含私有云(参数为edge)或云管(参数为cmp)的部署。本期亮点为KVM虚拟机对GPU的支持,不仅支持了虚拟机挂载图形模式的GPU

热文推荐