码迷,mamicode.com
首页 > 其他好文 > 详细

乐观锁 与 悲观锁

时间:2020-09-21 11:34:45      阅读:29      评论:0      收藏:0      [点我收藏+]

标签:思想   jvm   order   版本   行修改   size   失败   乐观锁   font   

什么是乐观锁?

很乐观的想,总是不会发生并发问题,每次取数据的时候总认为其他线程不会对数据进行修改,所以就不会加锁。但是在更新数据的时候,会判断在此之前又没有其他线程修改过数据。

实现乐观锁一般使用版本号机制和CAS操作。

版本号机制

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

CAS操作

即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

乐观锁的适用范围

适用于多读少写的场景

乐观锁的缺点

1 ABA 问题

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。

JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2 循环时间长开销大

自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

3 只能保证一个共享变量的原子操作

CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作。

 

什么是悲观锁?

总很很悲观的想,总是假设最坏的情况,每次取数据时认为其他线程会修改数据,所以加锁(读锁、写锁、行锁)防止数据被修改。当其他线程来想访问数据时,都需要线程阻塞挂起。可以通过数据库实现。

Java中synchronizedReentrantLock等独占锁就是悲观锁思想的实现。

悲观锁的适用范围

适用于多写少读的场景

乐观锁 与 悲观锁

标签:思想   jvm   order   版本   行修改   size   失败   乐观锁   font   

原文地址:https://www.cnblogs.com/zero-vic/p/13692262.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!