Java多线程并发中 CAS 的使用与理解

Wesley13
• 阅读 549

一、CAS (Compare And Swap):

  CAS(Compare And Swap),即比较并交换 CAS(V,E,N)。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——要更新的变量(V)、预期原值(E)和新值(N)。核心算法是如果V 值等于E 值,则将V 的值设为N 。若V 值和E 值不同,则说明已经有其他线程做了更新,则当前线程不做更新,直到V、E两个值相等,才更新V的值。
  1、代码演示:

/*
* 原子变量类:
*      AtomicBoolean
*      AtomicInteger
*      AtomicLong
*      AtomicReference
*
* 原子数组类:
*      AtomicLongArray
*      AtomicReferenceArray
*
* 原子方式更新对象中的字段类:
*      AtomicIntegerFieldUpdate
*      AtomicReferenceFieldUpdate
*
* 高并发汇总类:
*      LongAdder
*      LongAccumulator
*      DoubleAdder
*      DoubleAccumulator
*
*/
public class CASTest {

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger counter = new AtomicInteger(0);
        CountDownLatch c = new CountDownLatch(10000);

        for(int i = 0; i < 50; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.print( counter.incrementAndGet() + " ");            
                    c.countDown();
                }
            }).start();
        }
        c.await();
        System.out.println("AtomicInteger :" +  counter.get());
    }
}

二、CAS 中 ABA 问题:

  ABA 问题是指假设当前值为 A ,如果另一个线程先将 A 修改成 B , 再修改回成 A ,当前线程的 CAS 操作无法分辨当前值发生过变化。ABA 是不是一个问题与程序的逻辑有关,一般不是问题。而如果确实有问题,解决方法是使用 AtomicStampedReference ,在修改值的同时附加一个时间戳,只有值和时间戳都相同才进行修改。

  1、代码演示:

public class ABATest {
    public static void main(String[] args) throws InterruptedException {
        CASABATest();
        atomicStampedReferenceTest();
    }

    public static void atomicStampedReferenceTest(){
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(0, 0); // 第一个参数:引用对象的初始值,第二个参数:这个对象的初始标记

        new Thread(new Runnable() {
            @Override
            public void run() {         // compareAndSet(eR,nR,eS,nS) eR:引用对象的期望值,nR:引用对象的新值,eS:引用对象期望标记,nS:引用对象的新标记,当 eR、eS和当前线程的值相等才会修改eR、eS的值为nR、nS
                System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(0, 1, 0, 1) + ", atomicStampedReference value:" + atomicStampedReference.getReference());
            }
        },"Thread A").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(1, 0, 1, 2) + ", atomicStampedReference value:" + atomicStampedReference.getReference());
            }
        },"Thread B").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(0, 1, 0, 1) + ", atomicStampedReference value:" + atomicStampedReference.getReference());
            }
        },"Thread C").start();

    }

    /**
     * 演示CAS 更新的ABA 问题
     *
     */
    public static void CASABATest() throws InterruptedException {
        AtomicInteger integer = new AtomicInteger(0);

        new Thread(new Runnable() {
            @Override
            public void run() {          //compareAndSet(e,u) e:期望值,u:新值,如果e的值在当前线程中和integer的值一致,就把e的值修改成u的值
                System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(0, 1) + ", AtomicInteger value:" + integer); //true
            }
        },"Thread A").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(1 , 0) + ", AtomicInteger value:" + integer); //true
            }
        },"Thread B").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(0 , 1) + ", AtomicInteger value:" + integer); //true
            }
        },"Thread C").start();

    }
}

三、CAS 与 Synchronized 的对比:
  1.synchronized 是悲观的,它假设更新都是可能冲突的,所以要先获取锁,得到锁才更新,它是阻塞式算法,得不到锁就进入锁池等待。
  CAS 是乐观的,它假设冲突比较少,但使用CAS 更新,进行冲突检测,如果确实冲突就继续尝试直到成功,它是非阻塞式算法,有更新冲突就重试。

点赞
收藏
评论区
推荐文章
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Wesley13 Wesley13
2年前
java CAS compareAndSet, compareAndSwap 区别
之前看源代码的时候,发现了有两个方法。一个是compareAndSet,一个是compareAndSwap,傻傻分不清这两个到底哪个是CAS呢。我看了java并发编程实战找到了答案。CAS是一种计算机的指令。CAS包含3个操作数,需要读写的内存位置V,进行比较的值A和拟写入的新值B。当且仅当V的值等于A时,C
Wesley13 Wesley13
2年前
java多线程之CAS
CAS(CompareandSwap)CAS字面意思为比较并交换.CAS有3个操作数,分别是:内存值M,期望值E,更新值U。当且仅当内存值M和期望值E相等时,将内存值M修改为U,否则什么都不做。1.CAS的应用场景CAS只适用于线程冲突较少的情况。CAS的典型应用场景是:
Wesley13 Wesley13
2年前
Java多线程并发06——CAS与AQS
在进行更近一步的了解Java锁的知识之前,我们需要先了解与锁有关的两个概念CAS与AQS。关注我的公众号「Java面典」了解更多Java相关知识点。CAS(CompareAndSwap/Set)概念CAS函数,是比较并交换函数,它是原子操作函数。原理CA
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
Java并发机制底层实现原理
锁的膨胀过程预备知识CAS硬件对并发的支持在大多数处理器架构(包括IA32和Sparc)中采用的方法是实现一个比较并交换(CAS)指令,CAS包含了3个操作数——内存位置(V),预期原值(A),拟写入的新值(B),当且仅当VA时,CAS才会通过原子方式用新值(B)来更新(V)原有的值,无论操作成
Wesley13 Wesley13
2年前
Java多线程(二)
\恢复内容开始一,volatile关键字当多个线程操作共享数据时,可以保证内存中的数据可见性相较于synchronized关键字:1,不具备“互斥性”2,不能保证变量的原子性二,原子变量volatile保证内存可见性CAS(CompareAndSwap)算法保证数据的原子性内存值V预估值A更新值
Wesley13 Wesley13
2年前
ABA问题的本质及其解决办法
点击上方的蓝字关注我吧_程序那些事_简介CAS的全称是compareandswap,它是java同步类的基础,java.util.concurrent中的同步类基本上都是使用CAS来实现其原子性的。CAS的原理其实很简单,为了保证在多线程环境下我们的更新是符合预期的,或者说一个线程在更新某个对象的时
Wesley13 Wesley13
2年前
CAS机制与自旋锁
CAS(CompareandSwap),即比较并替换,java并发包中许多Atomic的类的底层原理都是CAS。它的功能是判断内存中某个地址的值是否为预期值,如果是就改变成新值,整个过程具有原子性。具体体现于sun.misc.Unsafe类中的native方法,调用这些native方法,JVM会帮我们实现汇编指令,这些指令是CPU的原子指令,因此
Wesley13 Wesley13
2年前
Java高并发程序设计(四)—— 无锁
一、无锁类的原理详解无锁的概念就是无障碍运行,无障碍是指所有的线程都能进入临界区,无锁在无障碍的基础上加上了一条就是每次竞争必然有一条能够胜出。理论上无障碍有可能线程都失败,所以无锁更切实可行,但实践上无障碍和无锁差不多。(一)CAS(CompareAndSwap)CAS算法的过程是这样的,它包含三个参数CAS(V,E,N)。V表示要更新的变