java里面的悲观锁和乐观锁

本文深入探讨了悲观锁和乐观锁的概念,分析了它们在数据库和Java中的应用。悲观锁基于锁定机制,确保数据的一致性和完整性,适用于高并发场景下的数据保护。乐观锁则依赖版本号或CAS算法,提高多读应用的吞吐量。

最近面试,面试官提到了悲观锁和乐观锁,感觉回答的不是很好,特此总结记录.
简单来说,悲观锁就是凡事都认为会出现最坏的情形,而乐观锁就是认为凡事都以最好的情形发展,对应一个消极,一个积极.

悲观锁

具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.(摘自百度百科)
传统关系型数据库里面的很多锁就是采用的这种机制,例如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。java里面的synchronize和ReentrantLock等重入锁就是采用的这种机制;

乐观锁

总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

乐观锁的两种实现方式:

1.版本号机制

即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “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(setversion=version+1 whereversion=1) 。
  4. 操作员 B 完成了数据录入操作,也将 version=1 的数据试图向数据库提交( balance=$80),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “提交版本必须等于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。

这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。
在这里插入图片描述

2.CAS算法

即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

需要读写的内存值 V
进行比较的值 A
拟写入的新值 B

当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。具体这块后续会继续深入了解,现在只简单提下.

参考博文:
原文链接:https://blog.youkuaiyun.com/qq_34337272/article/details/81072874
原文链接:https://blog.youkuaiyun.com/L_BestCoder/article/details/79298417

### Java悲观锁乐观锁的实现机制 #### 悲观锁的实现 悲观锁是一种基于锁的并发控制策略,其核心思想是通过加锁的方式确保数据的一致性完整性。在Java中,悲观锁可以通过`java.util.concurrent.locks.Lock`接口或直接使用`synchronized`关键字来实现。 在数据库层面,悲观锁通常通过SQL语句中的`SELECT ... FOR UPDATE`来实现。例如,以下代码展示了如何在MySQL中使用悲观锁[^3]: ```sql SELECT * FROM account WHERE id = 1 FOR UPDATE; ``` 上述SQL语句会锁定`account`表中`id=1`的记录,直到当前事务结束。其他事务如果尝试访问或修改这条记录,则会被阻塞。 在Java代码中,可以结合JDBC或ORM框架(如Hibernate)来实现悲观锁。以下是一个简单的示例,展示如何通过Hibernate实现悲观锁: ```java Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Account account = session.get(Account.class, 1L, LockMode.PESSIMISTIC_WRITE); account.setBalance(account.getBalance() - 100); tx.commit(); session.close(); ``` 上述代码中,`LockMode.PESSIMISTIC_WRITE`用于指定悲观写锁,确保在同一事务内对`account`对象的修改不会与其他事务冲突[^3]。 #### 乐观锁的实现 乐观锁假设数据冲突的概率较低,因此在操作时不会加锁,而是在提交更新时检查版本号是否发生变化。如果版本号未变,则允许更新;否则,更新失败并提示用户重试。 在Java中,乐观锁通常通过版本号字段(如`version`)来实现。以下是一个基于MyBatis的乐观锁实现示例[^2]: ```xml <update id="updateAccount" parameterType="Account"> UPDATE account SET balance = #{balance}, version = version + 1 WHERE id = #{id} AND version = #{version} </update> ``` 上述XML映射文件定义了一个更新语句,其中`version = version + 1`表示在更新成功后将版本号递增,而`WHERE id = #{
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值