乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。CAS是乐观锁。
CAS即比较并交换(compare and swap)。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS 操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值(V)与预期原值(A)相匹配,那么处理器会自动将该位置值更新为新值(B)。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了“我认为位置(V)应该包含值(A)。如果包含该值,则将新值(B)放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可”。Java 中sun.misc.Unsafe
类提供了硬件级别的原子操作来实CAS。java.util.concurrent包下大量的类都使用了这个Unsafe
类的 CAS 操作。
由于Unsafe
类涉及到内存操作,不安全,因此它被设计成只能通过Bootstrap ClassLoader来加载:
我们平时写的类都是Application ClassLoader加载的,是不能直接使用Unsafe类的。
CAS的伪代码:
while (true) {
oldValue = getDefaultValue();
// 如果CAS成功则跳出循环
if (CAS(oldValue, newValue)) {
break;
}
// 如果CAS失败则继续重新尝试CAS
}
CAS的底层是lock cmpxchg
指令(总线锁,其它 CPU 对内存的读写请求都会被阻塞,直到锁释放。因为锁总线的开销比较大,后来的处理器都采用锁缓存替代锁总线,在无法使用缓存锁的时候会降级使用总线锁),可以保证原子性。
CAS必须借助volatile
才能读取到共享变量的最新值来实现“比较并交换”的效果。
并发环境下,假设初始条件是A,去修改数据时,发现是A就会执行修改。但是看到的虽然是A,中间可能发生了A变B,B又变回A的情况.此时A已经非彼A,数据即使成功修改,也可能有问题。
可以通过AtomicstrapedReference解决ABA问题,它是一个带有标记的原子引用类,通过控制变量值的版本来保证CAS的正确性。
自旋CAS,如果一直循环执行,一直不成功,会给CPU带来非常大的执行开销。
解决方案:设置自旋次数,比如将上面的伪代码进行优化:
for (int i = 0; i < 1000; i++) {
oldValue = getDefaultValue();
// 如果CAS成功则跳出循环
if (CAS(oldValue, newValue)) {
break;
}
// 如果CAS失败则继续重新尝试CAS
}
CAS保证的是对一个变量执行操作的原子性,如果对多个变量操作时,CAS目前无法直接保证操作的原子性的。
可以通过这两个方式解决这个问题:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_43613793/article/details/120428745
内容来源于网络,如有侵权,请联系作者删除!