CAS&Atomic原子操作详解

75 阅读1分钟

原子操作和实现

image.png

Atomic

基于循环和CAS实现,特点线程不阻塞,适合并发冲突少的场景。 对于普通的Atomic类本文不在描述,实现较为简单。

AtomicMarkableReference

解决了ABA问题,只关心有没有改过

AtomicStampedReference

解决了ABA问题,不仅关心有没有改过,还关心被改过几次 示例:

public class UseAtomicStampedReference {
    static AtomicStampedReference<String> asr
            = new AtomicStampedReference("mark",0);

    public static void main(String[] args) throws InterruptedException {
        //拿到当前的版本号(旧)
        final int oldStamp = asr.getStamp();
        final String oldReference = asr.getReference();
        System.out.println(oldReference+"============"+oldStamp);

        Thread rightStampThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":当前变量值:"
                        +oldReference + "-当前版本戳:" + oldStamp + "-"
                  + asr.compareAndSet(oldReference,
                        oldReference + "+Java", oldStamp,
                        oldStamp + 1));
            }
        });

        Thread errorStampThread = new Thread(new Runnable() {
            @Override
            public void run() {
                String reference = asr.getReference();
                System.out.println(Thread.currentThread().getName()
                        +":当前变量值:"
                        +reference + "-当前版本戳:" + asr.getStamp() + "-"
                        + asr.compareAndSet(reference,
                        reference + "+C", oldStamp,
                        oldStamp + 1));
            }
        });
        rightStampThread.start();
        rightStampThread.join();
        errorStampThread.start();
        errorStampThread.join();

        System.out.println(asr.getReference()+"============"+asr.getStamp());
    }
}

// 结果
mark============0
Thread-0:当前变量值:mark-当前版本戳:0-true
Thread-1:当前变量值:mark+Java-当前版本戳:1-false
mark+Java============1

扩展

LongAdder

写热点分散,不能保证强一致,只是近似值

问题

ABA问题

image.png 这种问题大部分业务场景,如count++操作是没有影响的,但一定要知道这个问题。

衍生:订单下的ABA问题

image.png

image.png

image.png

循环时间长开销大

循环时间长,竞争激烈,它的效率就比synchronized低了(synchronized只有在竞争极小情况下才会采用偏向和轻量级锁)。

只能保证一个共享变量的原子操作

image.png