java并发编程 乐观锁和悲观锁

一、悲观锁

总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加(悲观)锁,不加锁就会出现缓存一致性问题。一旦加锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。

悲观锁在MySQL、Java有广泛的使用

MySQL的读锁、写锁、行锁等
Java的synchronized,ReentrantLock关键字

二、乐观锁

乐观锁顾名思义就是在操作时很乐观,认为操作不会产生并发问题(不会有其他线程对数据进行修改),因此不会上锁。但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制CAS(compare and swap)算法(比较和替换)实现。
简单理解:这里的数据,别想太多,你尽管用,出问题了算我怂,即操作失败后事务回滚、提示。

1.1 版本号机制
何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
1.1.1 实现套路:

取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
核心SQL:

update table set name = ‘Aron’, version = version + 1 where id = #{id} and version = #{version};

1.2 CAS算法
乐观锁的另一种技术技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试

CAS 操作中包含三个操作数 :

需要读写的内存位置V
进行比较的预期原值A
拟写入的新值B
如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值)。CAS 有效地说明了“ 我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。 ”这其实和乐观锁的冲突检查+数据更新的原理是一样的。

与锁相比,使用比较交换(CAS)会使代码看起来更加复杂一些。但由于其非阻塞性,它对死锁问题天生免疫,并且线程之间的相互影响也远远比基于锁的方式要小。更为重要的是,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程之间频繁调度带来的上下文切换开销,因此,它要比基于锁的方式拥有更优越的性能。

三、总结

读的多,冲突几率小,乐观锁。
写的多,冲突几率大,悲观锁。

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。乐观锁不能解决脏读的问题。乐观锁只是一种概念和思想,并不是真实存在的锁。通常,这种思想可以使用版本号机制CAS(compare and swap)算法(比较和替换)实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值