CAS详解

CAS详解

  1. synchronized关键字与Lock等锁机制都是悲观锁:无论做何种操作,首先都需要上锁,接下来再去执行后续操作,从而确保了接下来所有的操作都是由当前线程来执行的。
  2. 乐观锁:线程在操作之前不做任何预先的处理,而是直接去执行;当在最后执行变量更新的时候,当前线程需要有一种机制来确保当前被操作的变量是没有被其他线程修改的;CAS是乐观锁的一种极为重要的实现方式。

CAS(Compare And Swap)
比较与交换:这是一个不断循环的过程,一直到变量值被修改成功为止。CAS本身是由硬件指令来提供支持的,换句话说,硬件中通过一个原子指令来实现比较与交换;因此,CAS可以确保操作的原子性

对于CAS来说,其操作数只要涉及到如下三个:

  1. 需要被操作的内存值V
  2. 需要进行比较的值A
  3. 需要进行写入的值B

只有当V==A的时候,CAS才会通过原子操作的手段将V的值更新为B。

透过字节码分析变量操作的原子性

package com.learn.thread.cas;

public class CASTest {

    private int count;

    public int getCount() {
        return count;
    }

	// 读取 -> 修改 -> 写入:这三个操作并非原子操作
    public void increase() {
        ++this.count;
    }
}

javap获取字节码

  // access flags 0x1
  public increase()V
   L0
    LINENUMBER 12 L0
    ALOAD 0
    DUP
    GETFIELD com/learn/thread/cas/CASTest.count : I  // 读取,压入栈顶
    ICONST_1										 // 将1压入栈顶
    IADD											 // 将栈顶与栈顶下面的值相加
    PUTFIELD com/learn/thread/cas/CASTest.count : I  // 将值赋给count
   L1
    LINENUMBER 13 L1
    RETURN
   L2
    LOCALVARIABLE this Lcom/learn/thread/cas/CASTest; L0 L2 0
    MAXSTACK = 3
    MAXLOCALS = 1
}

使用synchronized关键字

package com.learn.thread.cas;

public class CASTest {

    private int count;

	// 保证可见性,其他线程读到count为最新值
    public synchronized int getCount() {
        return count;
    }

    public synchronized void increase() {
        ++this.count;
    }
}

  public synchronized void increase();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2                  // Field count:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field count:I
        10: return
      LineNumberTable:
        line 12: 0
        line 13: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/learn/thread/cas/CASTest;

使用synchronized并非最佳选择。

使用并发包中的原子类

package com.learn.thread.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);

        // 获取当前atomicInteger的值:5
        System.out.println(atomicInteger.get());
        // 返回旧值,将当前值设为8:5
        System.out.println(atomicInteger.getAndSet(8));
        // 获取当前atomicInteger的值:8
        System.out.println(atomicInteger.get());
        // 返回旧值,将当前值自增1:8
        System.out.println(atomicInteger.getAndIncrement());
        // 获取当前atomicInteger的值:9
        System.out.println(atomicInteger.get());
    }
}

程序结果

CAS底层实现

    public final int getAndSetInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
         // var1: 待操作对象的引用
         // var2:要操作的变量在当前对象中的内存偏移量
         // var5:变量的预期值A
         // var4:即将要写入的新的值
        } while(!this.compareAndSwapInt(var1, var2, var5, var4));

        return var5;
    }

关于CAS的限制或是问题

  1. 循环开销问题:并发量大的情况下会导致线程一直自旋。
  2. 只能保证一个变量的原子操作:可以通过AtomicReference来实现对多个变量的原子操作。
  3. ABA问题:1 -> 3, 3 -> 1。AtomicStampedReference解决(增加版本号)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值