并发编程篇-CAS技术
程序员文章站
2022-03-08 17:53:22
...
并发编程篇-CAS技术
CAS(Compare and Swap)
CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术
就是利用CPU的CAS指令:进行如下操作
1.从内存中读取数据(我们可以称之为旧值)
2.比较旧值和内存中的变量是否相同
3.如果相同,则进行新值的写入(写入1),返回成功,不相同则返回失败
Java并发包中的很多类都使用了CAS技术
先举个例子:
开启10000个线程,对【i】进行累加,如图
期望值是10000,然而由于多线程的影响,线程1和线程100可能同时读取到【i】的值为0,这样每个线程都对【0】进行加1,这样就会导致算出的结果不正确,结果可能会比预期值要小。如图
偶尔会出现不正确。
这就引出了一个概念【原子操作】,原子操作是指: “不可被中断的一个或者一系列操作”
上边的程序对【i】的操作就不是原子的,因为线程1在读取的时候,线程2可能获取到时间片进行读取写入
为了保证变量的正确累加,我们有很多种做法:
1.可以使用同步锁(如synchronized)
让线程排队的
线程执行完i++,才可以切换。如图
2.也可以使用循环CAS技术
举个例子: 比如i = 0 , 想对i进行+1
CAS:就是利用CPU的CAS指令:进行如下操作
1.从内存中读取数据(我们可以称之为旧值) 例如:0
2.比较旧值和内存中的变量是否相同 例如:发现0依然是0(哈哈)
3.如果相同,则进行新值的写入(写入1),返回成功,不相同则返回失败
(简而言之:就是比较并替换)
看到这大家可能有疑问:
1.旧值是否发生变化,什么意思?
如果多线程操作的话,可能就会导致旧值发生变化,例如
线程1读取到【0】,此时突然切换到线程2,线程2进行【加1】操作
切换回线程1,线程1会使用【0和1】比较,发现变化,则返回失败
2.如果比较发生变化=>返回失败,那么替换操作不执行, 这个操作岂不是白扔了?
所以我们会使用循环cas,也就是:
for( ;; ){
//一直cas,直到成功
if(cas成功){
break;
}
}
3.比较相等后,进行新值的替换。在此过程中 如果线程又切换了怎么办??
不会切换,看下图,紫色部分是原子操作,不可中断。
OK到这里,我们就可以改写上文中的代码,让【i】正确的累加
(使用AtomicInteger类,进行自增,因为这个类使用了循环CAS)
不信看源码(getAndIncrement方法):
然而CAS也并不是完美的,缺点
1、cpu开销大,在高并发下,许多线程,更新一变量,多次更新不成功,循环反复,给cpu带来大量压力。
2、只是一个变量的原子性操作,不能保证代码块的原子性。而锁能保证。
3、ABA问题
场景:张三取款,点了好几次取款操作。后台threadA和threadB工作,
此时threadA操作成功(100取款50===50元),threadB阻塞。
正好李四给张三转账50元(50 + 50->100),假设就这么巧!
之后threadB运行了,进行cas,比较(100==100) 于是又改为(100->50)。
·····钱丢了~~~(大家可以想想解决办法)
上一篇: 我是如何理解并使用maven的