乐观锁和悲观锁

        在 Java 中,乐观锁和悲观锁是处理并发访问的两种不同策略。它们用于在多线程环境下管理和控制对共享资源的访问,以确保数据的一致性和完整性。


悲观锁(Pessimistic Locking):

        悲观锁是一种较为保守的锁策略,它假设在数据被并发访问时会发生冲突。因此,在操作共享资源之前,悲观锁会将资源锁定,阻止其他事务对该资源进行访问或修改。
        在 Java 中,常见的实现悲观锁的方式包括数据库的行级锁(如使用 FOR UPDATE),以及使用 synchronized 关键字或 ReentrantLock 类等来保护共享资源,防止并发访问造成的数据不一致。

乐观锁(Optimistic Locking):

        乐观锁相信在大多数情况下,对共享资源的访问不会造成冲突。它不会在操作开始时对资源加锁,而是在操作结束时检查是否有其他线程对资源进行了修改。通常会使用版本号(versioning)或者时间戳(timestamp)等方式记录数据的变化情况。 CAS算法也通常用来做乐观锁问题。
        在 Java 中,乐观锁的实现通常涉及版本控制。比如,对于数据库操作,可以使用版本号字段来检查数据是否被其他事务修改,常见的实现方式包括在 SQL 语句中使用乐观锁的特定语法(如 UPDATE table SET ... WHERE version = current_version),或者在代码中手动比较版本号等方式来实现乐观锁。

版本号机制:

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

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

  1. 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。

  2. 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。

  3. 操作员 A 完成了修改工作,将数据版本号( version=1 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本等于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。

  4. 操作员 B 完成了操作,也将版本号( version=1 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。这样就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。

CAS算法

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

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

CAS 涉及到三个操作数:

  • V:要更新的变量值(Var)

  • E:预期值(Expected)

  • N:拟写入的新值(New)

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

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

  1. i 与 1 进行比较,如果相等, 则说明没被其他线程修改,可以被设置为 6 。

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

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

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

区别与适用场景:

1.悲观锁:适用于对并发冲突发生频率较高的场景,它假设并发冲突会频繁发生,因此在访问资源之前先对其进行加锁,确保数据操作的原子性和一致性。但是,悲观锁的使用可能会导致性能下降,因为资源被频繁锁定,其他线程无法立即访问资源。

2.乐观锁:适用于并发冲突发生较少的场景,它相信并发冲突的概率较低,因此在操作资源时不加锁,而是在操作结束时进行冲突检查。当冲突较少时,乐观锁的性能可能会更好,因为它减少了锁的竞争和等待。

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值