java并发之CAS

一、什么是CAS

  • CompareAndSwap(CAS),即比较并交换,它是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。

二、CAS的java实现

  • CAS并发原语体现在java语言中就是sun.misc.Unsafe类中的各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。
  • 由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程。原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。

三、java原子类

  • 原子类利用CAS 保证线程安全。
类型 具体类
Atomic* 基本类型原子类 AtomicInteger、AtomicLong、AtomicBoolean
Atomic*Array 数组类型原子类 AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
Atomic*Reference 引用类型原子类 AtomicReference、AtomicStampedReference、AtomicMarkableReference
Atomic*FieldUpdater 升级类型原子类 AtomicIntegerfieldupdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
Adder 累加器 LongAdder、DoubleAdder
Accumulator 积累器 LongAccumulator、DoubleAccumulator

四、CAS优缺点

  • CAS操作不会阻塞线程,如果CAS失败,可以一直进行尝试。可以减少线程上下文切换的消耗,但是如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
  • 当对一个共享变量执行操作时,我们可以使用循环CAS的方式保证原子操作,但是,对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。
  • CAS算法的实现需要读取内存中某时刻的数据并在当下时刻比较并交换,那么在比较并交换的过程中数据可能会变化。比如线程one从内存位置V中读出A,这时候线程two也从内存中读出A,并且线程two进行了一些操作将值改成了B,然后又将位置V的数据改成了A,这个时候线程one进行CAS操作发现内存位置V的数据仍然是A,然后线程one操作成功。 尽管线程one的CAS操作成功,但是并不代表这个过程就是没有问题的,这就是所谓的ABA问题。AtomicStampedReference原子类使用版本号来标记两次值相同的数据A,由于虽然值相同但版本号不同,从而规避了ABA问题。

五、代码

1.CAS实现一个自旋锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class SpinLockDemo {

    public AtomicReference<Thread> atomicThread = new AtomicReference<>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\tcomes in(1)");
        while (!atomicThread.compareAndSet(null, thread)){
        }
        System.out.println(Thread.currentThread().getName() + "\thas gotten spinLock(2)");
    }

    public void myUnLock() {
        Thread thread = Thread.currentThread();
        atomicThread.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\thas released spinLock(3))");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            spinLockDemo.myLock();
            try { TimeUnit.SECONDS.sleep(3L); } catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.myUnLock();
        }, "threaA").start();

        try { TimeUnit.SECONDS.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            spinLockDemo.myLock();
            try { TimeUnit.SECONDS.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); }
            spinLockDemo.myUnLock();
        }, "threaB").start();
    }
}

你可能感兴趣的:(java)