欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

并发编程篇-CAS技术

程序员文章站 2022-03-08 17:53:22
...

并发编程篇-CAS技术

CAS(Compare and Swap)

CAS(Compare-and-Swap),即比较并替换,是一种实现并发算法时常用到的技术
就是利用CPU的CAS指令:进行如下操作
1.从内存中读取数据(我们可以称之为旧值)  
2.比较旧值和内存中的变量是否相同             
3.如果相同,则进行新值的写入(写入1,返回成功,不相同则返回失败

Java并发包中的很多类都使用了CAS技术

并发编程篇-CAS技术

先举个例子:
开启10000个线程,对【i】进行累加,如图
并发编程篇-CAS技术
期望值是10000,然而由于多线程的影响,线程1和线程100可能同时读取到【i】的值为0,这样每个线程都对【0】进行加1,这样就会导致算出的结果不正确,结果可能会比预期值要小。如图
并发编程篇-CAS技术
偶尔会出现不正确。
并发编程篇-CAS技术
这就引出了一个概念【原子操作】,原子操作是指: “不可被中断的一个或者一系列操作”

上边的程序对【i】的操作就不是原子的,因为线程1在读取的时候,线程2可能获取到时间片进行读取写入
为了保证变量的正确累加,我们有很多种做法:
1.可以使用同步锁(如synchronized)
	让线程排队的 
	线程执行完i++,才可以切换。如图

并发编程篇-CAS技术

2.也可以使用循环CAS技术

举个例子:    比如i = 0 , 想对i进行+1 

CAS:就是利用CPU的CAS指令:进行如下操作
1.从内存中读取数据(我们可以称之为旧值)    例如:0
2.比较旧值和内存中的变量是否相同         例如:发现0依然是0(哈哈)    
3.如果相同,则进行新值的写入(写入1),返回成功,不相同则返回失败

(简而言之:就是比较并替换)

看到这大家可能有疑问:

1.旧值是否发生变化,什么意思?
    如果多线程操作的话,可能就会导致旧值发生变化,例如
	  线程1读取到【0】,此时突然切换到线程2,线程2进行【加1】操作
	  切换回线程1,线程1会使用【01】比较,发现变化,则返回失败
	
2.如果比较发生变化=>返回失败,那么替换操作不执行,	这个操作岂不是白扔了?
	所以我们会使用循环cas,也就是:
	for( ;; ){
		//一直cas,直到成功
		if(cas成功){
			break;
		}
	}
	
3.比较相等后,进行新值的替换。在此过程中 如果线程又切换了怎么办??
	不会切换,看下图,紫色部分是原子操作,不可中断。

并发编程篇-CAS技术
OK到这里,我们就可以改写上文中的代码,让【i】正确的累加
(使用AtomicInteger类,进行自增,因为这个类使用了循环CAS)
并发编程篇-CAS技术
不信看源码(getAndIncrement方法):
并发编程篇-CAS技术

然而CAS也并不是完美的,缺点

1、cpu开销大,在高并发下,许多线程,更新一变量,多次更新不成功,循环反复,给cpu带来大量压力。
2、只是一个变量的原子性操作,不能保证代码块的原子性。而锁能保证。
3、ABA问题
	场景:张三取款,点了好几次取款操作。后台threadA和threadB工作,
		此时threadA操作成功(100取款50===50元),threadB阻塞。
		正好李四给张三转账50元(50 + 50->100),假设就这么巧!
		之后threadB运行了,进行cas,比较(100==100) 于是又改为(100->50)。
     ·····钱丢了~~~(大家可以想想解决办法)