首页 > 其他分享 >如何实现乐观锁

如何实现乐观锁

时间:2023-11-05 10:32:39浏览次数:43  
标签:CAS 操作员 更新 版本号 乐观 如何 version 线程 实现

乐观锁一般会使用版本号机制或 CAS 算法实现,CAS 算法相对来说更多一些,这里需要格外注意。

#版本号机制

一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会加一。当线程 A 要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

举一个简单的例子:假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $120 。

  1. 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $120-$50 )。
  2. 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $120-$20 )。
  3. 操作员 A 完成了修改工作,将数据版本号( version=1 ),连同帐户扣除后余额( balance=$70 ),提交至数据库更新,此时由于提交数据版本等于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
  4. 操作员 B 完成了操作,也将版本号( version=1 )试图向数据库提交数据( balance=$100 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。

这样就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。

#CAS 算法

CAS 的全称是 Compare And Swap(比较与交换) ,用于实现乐观锁,被广泛应用于各大框架中。CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。

CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令。

原子操作 即最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。

CAS 涉及到三个操作数:

  • V:要更新的变量值(Var)
  • E:预期值(Expected)
  • N:拟写入的新值(New)

当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值。如果不等,说明已经有其它线程更新了 V,则当前线程放弃更新。

举一个简单的例子:线程 A 要修改变量 i 的值为 5,i 原值为 2(V = 2,E=2,N=5,假设不存在 ABA 问题)。

  1. i 与 2 进行比较,如果相等, 则说明没被其他线程修改,可以被设置为 5 。
  2. i 与 2 进行比较,如果不相等,则说明被其他线程修改,当前线程放弃更新,CAS 操作失败。

当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

Java 语言并没有直接实现 CAS,CAS 相关的实现是通过 C++ 内联汇编的形式实现的(JNI 调用)。因此, CAS 的具体实现和操作系统以及 CPU 都有关系。

sun.misc包下的Unsafe类提供了compareAndSwapObjectcompareAndSwapIntcompareAndSwapLong方法来实现的对Objectintlong类型的 CAS 操作

/**
	*  CAS
  * @param o         包含要修改field的对象
  * @param offset    对象中某field的偏移量
  * @param expected  期望值
  * @param update    更新值
  * @return          true | false
  */
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);

public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);

public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);

标签:CAS,操作员,更新,版本号,乐观,如何,version,线程,实现
From: https://blog.51cto.com/u_14299064/8190948

相关文章

  • 自然语言处理历史史诗:NLP的范式演变与Python全实现
    本文全面回顾了自然语言处理(NLP)从20世纪50年代至今的历史发展。从初创期的符号学派和随机学派,到理性主义时代的逻辑和规则范式,再到经验主义和深度学习时代的数据驱动方法,以及最近的大模型时代,NLP经历了多次技术革新和范式转换。文章不仅详细介绍了每个阶段的核心概念和技术,还提供......
  • 百度网站怎样优化排名_百度如何优化排名靠前
    网站如何优化排名才会靠前?1、(1)用户体验经过网站的优化设计,用户可以方便地浏览网站的信息、使用网站的服务。2、④、内链建设:站内优化也是重中之重,如导航栏设计、站内导向链接规划等。当用户浏览网页的时候最好知道自己身处在什么位置。另外,为用户提供有价值的“下一站”跳转......
  • 高精度减法(C语言实现)
    高精度减法(C语言实现)介绍众所周知,整数在C和C++中以int,long,longlong三种不同大小的数据存储,数据大小最大可达2^64,但是在实际使用中,我们仍不可避免的会遇到爆longlong的超大数运算,这个时候,就需要我们使用高精度算法,来实现巨大数的运算。高精度的本质是将数字以字符串的形式......
  • 如何防止PHP中的SQL注入?
    内容来自DOChttps://q.houxu6.top/?s=如何防止PHP中的SQL注入?如果没有对用户输入进行任何修改就插入到SQL查询中,那么应用程序就会容易受到SQL注入攻击,就像以下示例中的那样:$unsafe\_variable=$\_POST['user\_input'];mysql\_query("INSERTINTO`table`(`column`)VAL......
  • 项目管理之如何估算项目工作成本
    在项目管理中,如何估算项目工作成本是一个关键问题。为了解决这个问题,我们可以采用自上而下的成本限额估算法和自下而上的成本汇总估算法。这两种方法各有优缺点,但都可以帮助我们准确地估算项目工作成本。自上而下的成本限额估算法自上而下的成本限额估算法是一种常用的估算方法。它......
  • 面试—如何介绍项目中的多级缓存?
    项目中使用的多级缓存也就是分布式缓存Redis+本地缓存Caffeine,那么令Caffeine作为一级缓存,Redis作为二级缓存,在项目中通过记录数据的访问次数,将热点数据放在本地缓存,将非热点数据放在Redis缓存中,访问流程如下:使用多级缓存的好处在于Redis单机每秒可以接收10w次的......
  • 如何通过一条命令让Linux系统崩溃
    如何一条命令让 Linux 系统崩溃,如何一条命令让系统不可用,如何一条命令清除所有数据。这个对于要离职的人来讲,是必须要学会的。即将离职的程序员,如果比较负责任的话,应该在离开前,将自己不再使用的电脑清理干净,如何一条命令清理硬盘,且看如下示例,用随机数擦除存储 sda中的数据:dd......
  • c++实现排序算法
    排序算法选择排序#include<iostream>#include<cmath>usingnamespacestd;intmain(){ intn,i,j,a[2000]; boolt; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<n;i++) for(j=i+1;j<=n;j++) if......
  • 如何进行Linux中RedHat 7.6 安装
    今天就跟大家聊聊有关如何进行Linux中RedHat7.6安装,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。开始安装为了日后排查问题时,不出现乱码,所以此处选择英文配置安装选项选择需要安装的包单击“SOFTWARES......
  • SpringBoot系列之MyBatis Plus自动填充实现
    系列博客专栏:SpringBoot2.0系列博客专栏开发环境JDK1.8SpringBoot2.2.1Maven3.2+Mysql5.7.36开发工具IntelliJIDEAsmartGit项目场景在项目中经常会遇到需要自动填充数据的情况,比如新增一个DO类,里面可能会有id、create_time、modify_time、create_u......