什么是CAS/ABA以及volatile的使用

1.CAS原理介绍

CAS全称 比较和交换(Conmpare And Swap),它是一种思想,是乐观锁。

内存地址V,内存地址V中旧值C,获取的旧预期值A,要修改的新值B, 工作内存W

我们需要把C修改成B时

首先从V中取到值进入W中时,不像悲观锁一样,把V中值锁住,而是先把A放入W中,

然后用A和C进行比较,如果不一样,则修改失败。

然后重新从V中获取A,这个过程叫自旋,使用了自旋锁。

再次进行比较,如果此次获取的A和C一样,则修改成功,最后进行SWAP更新C。

2.ABA介绍

因为CAS需要在操作值的时候检查下值有没有发生变化,

如果没有发生变化则更新,

但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,

但是实际上却变化了。

ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

如果A==A && 1!=3 时 修改失败

如果A==A && 1==1时 修改成功

如果A!=B && 1!=2 时  自旋

 

3.CAS缺点

  cpu开销大,因为如果自旋长时间不成功,给CPU带来很大压力

 不能保证代码块原子性: 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。因为没有锁机制,当有三个变量一起操作时,如果有一个修改失败了,就会影响最后的结果,就不得不使用锁机制了。

 可见性问题:当多个线程一起并发操作时,同时更新修改,可能存在可见性不及时现象。

指令重排序问题: 一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,

它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,

但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。(基本判断方法是检查上下代码是否有数据依赖)

例如:单例模式中,并发高的时候指令重排序可能会导致创建空对象情况。

4.volatile的使用

volatile关键字使用了CAS原理,并进行了优化。

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

  1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(它会强制将对缓存的修改操作立即写入主存;如果是写操作,它会导致其他CPU中对应的缓存行无效)

  2)禁止进行指令重排序。(它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成)

缺点 :对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。(当取到值的时候,再进行计算后,还没有及时更新,其他线程可能已经多次修改这个值了)

优点:效率高于synchronized。

使用volatile必须具备以下2个条件:

  1)对变量的写操作不依赖于当前值

  2)该变量没有包含在具有其他变量的不变式中

使用实例:

1 public class Singleton {
2	
3	private volatile static Singleton instance = null; //
4
5	private Singleton() {
6	};
7
8	public static Singleton getInstance() {
9
10		if (instance == null) {
11
12			synchronized (Singleton.class) {
13				if (instance == null) {
14					instance = new Singleton();
15				}
16			}
17		}
18		return instance;
19	}
20
21}

  使用  volatile 原因:

  1)  第14行代码new出来的对象,别的线程立马可见,避免进入第10行代码。

  2)  对于第14行 instance = new Singleton(); 

          可以分解为3个步骤:
         1 memory=allocate();// 分配内存 相当于c的malloc
         2 ctorInstanc(memory) //初始化对象
         3 instance =memory //设置instance 指向刚分配的地址

           上面的代码在编译器运行时,2 3步半初始化过程,可能会出现重排序 从1-2-3 排序为1-3-2

           如此在多线程下就会出现问题

           例如现在有2个线程A,B

            线程A在执行第5行代码时,B线程进来,而此时A执行了 1和3,没有执行2,此时B线程判断s不为null 直接返回一个未初             始化的对象,就会出现问题.

 加上volatile就是为了防止产生指令的重排序问题.

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章