乐观锁与悲观锁

xiaoxiao2021-03-01  10

【什么是乐观锁与悲观锁?】 乐观锁: 乐观地看待事物,每次去取数据的时候都认为别人不会在此期间对数据进行修改,所以不上锁。但是在更新的时候会判断一下别人是否对数据改动过,若判断为改动过,则重新取数据、重新判断改动。若判断为未改动过,则更新数据。 其中,判断是否发生改动有两种实现方式:版本号机制和CAS算法

悲观锁: 悲观地看待事物,每次去取数据的时候都认为别人会在此期间对数据进行修改,所以拿数据都会上锁。别人想拿到这个数据就会阻塞到他拿到锁。 传统数据库用到了很多悲观锁机制,如行锁,表锁,读锁,写锁,都是在操作之前先上锁。java中为保证线程安全的synchronized和ReentrantLock也是悲观锁的实现。

【乐观锁与悲观锁的应用场景】

乐观锁适用场景悲观锁适用场景多读场景多写场景当读写冲突发生的很少时,采用乐观锁可以省去加锁的开销,提高整个系统的效率。若冲突发生很多的时候,会使得不停地进行尝试,反而降低了效率当冲突发生的很多时,直接采用悲观锁,可以有效地保证线程安全,

【乐观锁的两种实现方式】

方式1:版本号机制 通常在数据表多加一列版本号(version)字段,用于表示数据被修改次数。 当数据被修改一次,version+1。当一条线程对数据进行更新时,在读取数据的同时也会读取其version,提交更新时,对比version是否相同。 若相同,则完成更新,反之,重新尝试更新操作,直至成功。

方式2:CAS算法 CAS:compare and swap CAS算法实现了在不使用锁的情况下实现线程之间的变量同步。CAS涉及三个操作数:

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

当V == A时,CAS通过原子方式用B替代V,否则不会执行任何操作(这种情况下,会再次尝试)

【乐观锁的缺点】

1.ABA问题: 如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。 解决方式: JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2.循环时间长开销大

自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。

3.只能保证一个共享变量的原子操作 CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。 当有多个共享变量时,可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作。

参考资料,Snailclimb大神

转载请注明原文地址: https://www.6miu.com/read-3850324.html

最新回复(0)