【Java】之 CAS

一、定义


CAS(compare and swap),是一种多线程无锁的编程方式。

当修改多个线程可能同时操作的属性时,给定期望值要更新的值,如果当前值期望值一致,则更新为要设置的值

CAS的底层是通过将读取-比较-设置三个操作作为一个指令执行,在执行期间不会有其他线程改变变量。这保证了操作的原子性。

CAS其以乐观的态度进行操作,不断循环等待进行,对比而言,线程切换需要 8万个cup时钟周期,而循环重试只需要 几个cup时钟。

CAS需要三个参数:

  • offset(内存地址)
  • expect(期望值)
  • update(需要更新的值)
    操作成功返回true,否则返回false。


二、测试并使用


1. synchronized
public class SynchronizedTest {

    static int count = 0;

    public static void main(String[] args) throws InterruptedException {

        final Object lock = new Object();

        Thread[] ths = new Thread[10000];
        for(int i=0;i<ths.length;i++) {
            Thread thread = new Thread(new Runnable() {

                public void run() {
                    for(int i=0;i<10000;i++) {
                        synchronized (lock) {
                            count ++;
                        }
                    }
                }
            });
            ths[i] = thread;
        }
        long begin = System.currentTimeMillis();

        for(int i = 0; i < ths.length; i++) {

            ths[i].start();
            ths[i].join();
        }
        System.out.println(count);// 100000000
        System.out.println(System.currentTimeMillis() - begin); //4073
    }
}


2. cas
public class CASTest {

    static final AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {

        Thread ths [] = new Thread[10000];
        for(int i=0;i<ths.length;i++) {
            Thread thread = new Thread(new Runnable() {

                public void run() {
                    for(int i=0;i<10000;i++) {
                        count.incrementAndGet();
                    }

                }
            });
            ths[i] = thread;
        }
        long begin = System.currentTimeMillis();

        for(int i = 0; i < ths.length; i++) {

            ths[i].start();
            ths[i].join();
        }

        System.out.println(count.get());// 100000000
        System.out.println(System.currentTimeMillis() - begin); // 1785
    }
}

3. 由此可看出

CAS 性能要比 Synchronized 快上 2.8倍



三、问题


(1) 循环时间过长

若一直不成功, 则会一直尝试, 时间过长, 可能会给CPU带来很大的开销


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

只针对单一变量, 有其局限性


(3) ABA 问题

假如链表为: head->A->B->C,线程t1要将head->B,cas(A,B),此过程中线程t2进行了head->A->C->D,此时B已经处于游离状态,B.next=null,切换到线程t1,t1发现header还是A,就进行交换
head->B,这时链表丢失了C和D。
以上就是由于ABA问题带来的隐患,各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题,在Java中,AtomicStampedReference也实现了这个功能。

private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0); //初始值为100,初始时间戳为0
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1); //cas 还要判断时间戳
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);


四、参考资料


  1. https://zhuanlan.zhihu.com/p/34556594
  2. <<码出高效>>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值