Java并发编程之CAS原理

149 阅读3分钟

原理简介

CAS全称是Compare-And-Swap,比较交换的意思。它的作用是比较内存中的某个值是否和预期的一致,是的话就更新成新的值,不是的话就一直循环。

考虑下面一种场景:变量a的初始值是10,有两个线程要对它进行操作

线程一:

  • 获取变量a的值10
  • 10进行加1操作得到11
  • 11写回变量a

线程二:

  • 获取变量a的值10
  • 10进行减1操作得到9
  • 9写回变量a

当线程一执行完第一步时,如果线程二也执行了第一步,那么两个线程获取到的a的值就都是10,接着线程1进行加1操作变成11并写回变量,线程二进行减一操作变成了9并写回变量,这时就会把11覆盖掉变成9,并不是预期的10

采用CAS方式的操作如下:

线程一:

  • 获取变量a的值10
  • 10进行加1操作得到11
  • 判断a的值是不是10,如果是就把11写回变量a;如果不是就重新执行第一步

线程二:

  • 获取变量a的值10
  • 10进行减1操作得到9
  • 判断a的值是不是10,如果是就把9写回变量a;如果不是就重新执行第一步

采用CAS方式的区别在于,将结果写回变量之前,先判断了变量的值是不是一开始获取到的值,如果是的话说明当前线程修改期间没有其他线程对其进行操作,可以放心把结果写回变量内存。如果不是的话,说明有其他线程对变量进行了修改,所以就重新去获取变量的值,重新执行操作,一直循环去判断。

需要注意的是,在执行CAS操作时,一个线程需要获取到其他线程对变量的操作结果,但由于Java内存模型(JMM)的设置,线程操作时是在本地内存中,要想保证线程间的可见性,变量必须使用volatile修饰。

缺点

  • 因为CAS是通过循环的方式保证了操作的原子性,但是如果循环次数过多就会造成资源消耗过多;
  • CAS操作只是针对一个变量的操作原子性,保证了其操作的结果正确,并不能保证多个变量或代码块的原子性;
  • CAS操作会产生ABA问题

ABA问题

CAS操作进行比较时只是判断和预期值是否相等,并不关心是否有其他线程对其修改过,比如把变量值从A改成B,由把B改成A,最终获取时判断没有问题,但是中间已经被修改过,这就是ABA问题。

要想解决ABA问题,可以给变量添加一个版本号,每次修改变量都更新版本号,这样就可以通过版本号来判断变量是否被修改过。