#### 前言
在面试中,并发线程平安发问必然是不会短少的,那根底的CAS原理也必须理解,这样在面试中能力加分,那来看看面试可能会问那些问题:
- 什么是乐观锁与乐观锁
- 什么乐观锁的实现形式-CAS(Compare and Swap),CAS(Compare and Swap)实现原理
- 在JDK并发包中的应用
- CAS的缺点
1. 什么是乐观锁与乐观锁?
乐观锁
总是假如最坏的状况,每次读取数据的时候都默认其余线程会更改数据,因而须要进行加锁操作,当其余线程想要拜访数据时,都须要阻塞挂起。乐观锁的实现:
- 传统的关系型数据库应用这种锁机制,比方行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁;
- Java外面的同步
synchronized
关键字的实现。
乐观锁
乐观锁,其实就是一种思维,总是认为不会产生并发问题,每次读取数据的时候都认为其余线程不会批改数据,所以不上锁,然而在更新的时候会判断一下在此期间别的线程有没有批改过数据,乐观锁实用于读操作多的场景,这样能够进步程序的吞吐量。实现形式:
- CAS实现:Java中java.util.concurrent.atomic包上面的原子变量应用了乐观锁的一种CAS实现形式,CAS剖析看下节。
- 版本号管制:个别是在数据表中加上一个数据版本号version字段,示意数据被批改的次数,当数据被批改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若方才读取到的version值为以后数据库中的version值相等时才更新,否则重试更新操作,直到更新胜利
乐观锁实用于读多写少的状况下(多读场景),乐观锁比拟实用于写多读少场景
2. 乐观锁的实现形式-CAS(Compare and Swap),CAS(Compare and Swap)实现原理
背景
在jdk1.5之前都是应用synchronized
关键字保障同步,synchronized
保障了无论哪个线程持有共享变量的锁,都会采纳独占的形式来拜访这些变量,导致会存在这些问题:
- 在多线程竞争下,加锁、开释锁会导致较多的上下文切换和调度延时,引起性能问题
- 如果一个线程持有锁,其余的线程就都会挂起,期待持有锁的线程开释锁。
- 如果一个优先级高的线程期待一个优先级低的线程开释锁,会导致优先级倒置,引起性能危险
为了优化乐观锁这些问题,就呈现了乐观锁:
假如没有并发抵触,每次不加锁操作同一变量,如果有并发抵触导致失败,则重试直至胜利。
CAS(Compare and Swap)原理
CAS 全称是 compare and swap(比拟并且替换),是一种用于在多线程环境下实现同步性能的机制,其也是无锁优化,或者叫自旋,还有自适应自旋。
在jdk中,CAS
加volatile
关键字作为实现并发包的基石。没有CAS就不会有并发包,java.util.concurrent中借助了CAS指令实现了一种区别于synchronized的一种乐观锁。
乐观锁的一种典型实现机制(CAS):
乐观锁次要就是两个步骤:
- 冲突检测
- 数据更新
当多个线程尝试应用CAS同时更新同一个变量时,只有一个线程能够更新变量的值,其余的线程都会失败,失败的线程并不会挂起,而是告知这次竞争中失败了,并能够再次尝试。
在不应用锁的状况下保障线程平安,CAS实现机制中有重要的三个操作数:
- 须要读写的内存地位(V)
- 预期原值(A)
- 新值(B)
首先先读取须要读写的内存地位(V),而后比拟须要读写的内存地位(V)和预期原值(A),如果内存地位与预期原值的A相匹配,那么将内存地位的值更新为新值B。如果内存地位与预期原值的值不匹配,那么处理器不会做任何操作。无论哪种状况,它都会在 CAS 指令之前返回该地位的值。具体能够分成三个步骤:
- 读取(须要读写的内存地位(V))
- 比拟(须要读写的内存地位(V)和预期原值(A))
- 写回(新值(B))
3. CAS在JDK并发包中的应用
在JDK1.5以上 java.util.concurrent(JUC java并发工具包)是基于CAS算法实现的,相比于synchronized独占锁,梗塞算法,CAS是非梗塞算法的一种常见实现,应用乐观锁JUC在性能上有了很大的晋升。
CAS如何在不应用锁的状况下保障线程平安,看并发包中的原子操作类AtomicInteger::getAndIncrement()办法(相当于i++的操作):
// AtomicInteger中//value的偏移量private static final long valueOffset; //获取值private volatile int value;//设置value的偏移量static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }//减少1public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }
- 首先value必须应用了volatile润饰,这就保障了他的可见性与有序性
- 须要初始化value的偏移量
unsafe.getAndAddInt通过偏移量进行CAS操作,每次从内存中读取数据而后将数据进行+1操作,而后对原数据,+1后的后果进行CAS操作,胜利的话返回后果,否则重试直到胜利为止。
//unsafe中public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { //应用偏移量获取内存中value值 var5 = this.getIntVolatile(var1, var2); //比拟并value加+1 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;}
JAVA实现CAS的原理,unsafe::compareAndSwapInt是借助C来调用CPU底层指令实现的。上面是sun.misc.Unsafe::compareAndSwapInt()办法的源代码:
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
4. CAS的缺点
ABA问题
在多线程场景下CAS会呈现ABA问题,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下
线程1,期望值为A,欲更新的值为B
线程2,期望值为A,欲更新的值为B
线程3,期望值为B,欲更新的值为A
- 线程1领先取得CPU工夫片,而线程2因为其余起因阻塞了,线程1取值与冀望的A值比拟,发现相等而后将值更新为B,
- 这个时候呈现了线程3,线程3取值与冀望的值B比拟,发现相等则将值更新为A
- 此时线程2从阻塞中复原,并且取得了CPU工夫片,这时候线程2取值与冀望的值A比拟,发现相等则将值更新为B,尽管线程2也实现了操作,然而线程2并不知道值曾经通过了A->B->A的变动过程。
ABA问题带来的危害:
小明在提款机,提取了50元,因为提款机问题,有两个线程,同时把余额从100变为50
线程1(提款机):获取以后值100,冀望更新为50,
线程2(提款机):获取以后值100,冀望更新为50,
线程1胜利执行,线程2某种原因block了,这时,某人给小明汇款50
线程3(默认):获取以后值50,冀望更新为100,
这时候线程3胜利执行,余额变为100,
线程2从Block中复原,获取到的也是100,compare之后,持续更新余额为50!!!
此时能够看到,理论余额应该为100(100-50+50),然而实际上变为了50(100-50+50-50)这就是ABA问题带来的胜利提交。
解决办法
- AtomicStampedReference: 带有工夫戳的对象援用来解决ABA问题。这个类的compareAndSet办法作用是首先查看以后援用是否等于预期援用,并且以后标记是否等于预期标记,如果全副相等,则以原子形式将该援用和该标记的值设置为给定的更新值。
public boolean compareAndSet( V expectedReference,//预期援用 V newReference,//更新后的援用 int expectedStamp, //预期标记 int newStamp //更新后的标记)
- version:在变量后面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A
循环工夫长开销大
自旋CAS(不胜利,就始终循环执行,直到胜利)如果长时间不胜利,会给CPU带来极大的执行开销。
解决办法:
- 限度自旋次数,避免进入死循环
- JVM能反对处理器提供的pause指令那么效率会有肯定的晋升,
只能保障一个共享变量的原子操作
当对一个共享变量执行操作时,咱们能够应用循环CAS的形式来保障原子操作,然而对多个共享变量操作时,循环CAS就无奈保障操作的原子性
解决办法:
- 如果须要对多个共享变量进行操作,能够应用加锁形式(乐观锁)保障原子性,
能够把多个共享变量合并成一个共享变量进行CAS操作。
各位看官还能够吗?喜爱的话,动动手指导个????,点个关注呗!!谢谢反对!
欢送扫码关注,原创技术文章第一工夫推出