哥有故事,你有酒,长夜漫漫,听我给你说。
参考资源:
https://blog.csdn.net/hsuxu/article/details/9467651
1.概述
CAS,compare and swap ,“比较交换”的意思。它是一种并发状态下的,比较交换的策略。
想必,我们一定听说过乐观锁的概念,并发中乐观锁的核心概念就是应用了CAS。它包含了三个值:内存值,预期值和更新值。当内存值和预期值相等时,就会使用更新值将原来的数据(预期值)进行更新;如果不相等,则什么都不做。
2.例子
举个经典的例子,帮助大家理解。(伪代码)
1 public class AtomicInt { 2 private volatile int value; 3 public final int get() { 4 return value; 5 } 6 7 publicfinal int getAndIncrement() {
// 下面的for的无限循环,就是经典的CAS自旋(Compare and swap) 8 for (;;) { 9 int current = get(); 10 int next = current + 1; 11 if (compareAndSet(current, next)) 12 return current; 13 } 14 } 15 16 public final boolean compareAndSet(int expect, int update) { 17 unsafe.compareAndSwapInt方法(方法内部,使用JNI调用C的代码); 18 } 19 }
3.凡事问个为什么.
为什么要比较?为什么要用CAS的自旋?直接设值不行吗?它有什么优点缺点?
例如,i++ 这个简单的操作不是一步完成的,而是分了三步。第一步,取值,第二步加一,第三部更新值。
假设有A,B两个线程同时操作i++这个处理,那么,当线程A完成上述第二步的时候,线程B已经将I的值更新(第三步)做完了。这样就会导致值发生异常,就是所谓的线程不安全。
所以,利用了乐观锁的思想采用了自旋的方式,每一次,就会先取得加一后的值,再将旧的值和内存中的值进行比较,如果相等,则说明,没有被别的线程动过,因此可以正常更新;如果不相等,则说明值已经被更新了,放弃本次的操作,从头再来,再重新取值,加一,更新。
CAS自旋(乐观锁)避免了悲观锁独占的现象,同时提高了并发的性能。但是,它也是有缺点的。(第三点参考了其他文章)
①乐观锁只能保证一个变量的的原子操作,多个变量的话,就没有办法了。
②长时间自旋,导致CPU消耗过大。
③ABA问题。CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但是,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上,被其他线程改过。这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。
4. 原子类Atomic
现在习惯多去查api文档(这是个好的习惯)。在JUC的atomic包下有如下几个类:
AtomicBoolean
AtomicInteger
AtomicLong
AtomicMarkableReference
AtomicReference
它们的原理都是应用了CAS的自旋,这几个类在并发编程中经常用到,它们都是线程安全的类。
就聊这么多,祝君好梦!