乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

Posted 四猿外

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乐观锁和悲观锁的理解及如何实现,有哪些实现方式?相关的知识,希望对你有一定的参考价值。

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制, 比如行锁,表锁等,读锁, 写锁等,都是在做操作之前先上锁。再比如Java 里面的同步原语synchronized 关键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观, 每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition 机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式CAS 实现的。

乐观锁的实现方式:

1、使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识, 不一致时可以采取丢弃和再次尝试的策略。

2、java 中的Compare and Swap 即CAS ,当多个线程尝试使用CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败, 失败的线程并不会被挂起,而是被告知这次竞争中失败, 并可以再次尝试。CAS 操作中包含三个操作数—— 需要读写的内存位置( V)、进行比较的预期原值( A)
和拟写入的新值(B)。如果内存位置V 的值与预期原值A 相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。

CAS 缺点:

1、ABA 问题:
比如说一个线程one 从内存位置V 中取出A,这时候另一个线程two 也从内存中取出A,并且two 进行了一些操作变成了B,然后two 又将V 位置的数据变成A,这时候线程one 进行CAS 操作发现内存中仍然是A,然后one 操作成功。尽管线程one 的CAS 操作成功,但可能存在潜藏的问题。从Java1.5 开始JDK 的atomic包里提供了一个类AtomicStampedReference 来解决ABA 问题。

2、循环时间长开销大:
对于资源竞争严重(线程冲突严重) 的情况, CAS 自旋的概率会比较大, 从而浪费更多的CPU 资源,效率低于synchronized。

3、只能保证一个共享变量的原子操作:
当对一个共享变量执行操作时,我们可以使用循环CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环CAS 就无法保证操作的原子性, 这个时候就可以用锁。

以上是关于乐观锁和悲观锁的理解及如何实现,有哪些实现方式?的主要内容,如果未能解决你的问题,请参考以下文章

乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

java中悲观锁和乐观锁的区别

数据库乐观锁和悲观锁的理解和实现

悲观锁和乐观锁的区别以及实现方式

乐观锁的实现

协作式原创查漏补缺之乐观锁与悲观锁TODO