Java 多线程(四):CAS 机制
一、CAS 机制简介
CAS机制即Compare And Set,核心的思想是:比较和替换。
简单来说就是:使用一个期望值来和当前变量的值进行比较,如果当前的变量值与我们期望的值相等,就用一个新的值来更新当前变量的值。
CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时(条件),将内存值修改为B并返回true,否则条件不符合返回false。条件不符合说明该变量已经被其它线程更新了。
当多个线程访问相同的数据时,如果使用锁来进行并发控制,当某一个线程(T1)抢占到锁之后,那么其他线程再尝试去抢占锁时就会被阻塞,当T1释放锁之后,下一个线程(T2)再抢占到锁后并且重新恢复到原来的状态线程从阻塞到重新准备运行有较大的时间开销。
而假设我们业务代码本身并不具备很复杂的操作,并发量也不高,那么当我们使用CAS机制来代替加锁操作,当多个线程操作同一变量时每个线程会首先会读取到地址值对应的原值作为自己的期望值,然后进行操作,操作完以后在更新的时候他会判断现在地址值对应的值是否与自己的期望值相同,如果相同,就认为自己操作的过程中别人没有进行过操作。则将自己操作后的值更新到地址值对应的位置,如果不同则说明自己操作的过程中一定有其他的线程更新过数据,然后会把当前地址值对应的值返回给用户,用户拿到新值重新上次的操作。不断循环直到更新成功。
二、CAS 机制用途
实现了乐观锁并提供了Atomic类操作
CAS乐观锁(循环内自旋):原理:A=内存值,thread1对A进行累加操作后的值为B。更新内存值时会判断A和B是否相等,如果相等,那么B替换A退出循环,如果不相等,重新获得内存值,进行操作。此套流程如何保证内存值是最新的?详见volatile原理此套流程如何保证V,A比较B替换V时是原子操作?cas底层用unsafe直接访问底层操作系统,做了硬件级别的原子操作。
java.util.concurrent.atomic包下的原子操作类都是基于CAS实现的。
三、CAS机制的优点与问题
1. 优点
在并发量不大,且执行的任务不是耗时任务的时候,会提高运行效率。
2. 缺点
循环开销大:如果CAS长时间执行不成功,则会给CPU带来交大的执行开销。
只能保证一个共享变量的原子操作:如果需要对多个共享变量进行同步,就得使用锁,或者将几个共享变量封装起来,使用CAS来进行同步。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。
ABA问题:相信大家都知道ABA问题的意思是什么。这里我们着重提一下如何解决ABA问题,解决方案就是对内存中的值加个版本号,在比较的时候除了比较值还的比较版本号,其中jdk中AtomicReference虽实现了原子操作,但是就存在ABA问题,所以AtomicStampedReference就用版本号实现CAS机制,并避免了ABA问题。
四、CAS机制与其他机制的对比