• 并发编程(三)原子操作CAS


    1.CAS(Compare And Swap)原子操作:

    假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要么完全不执行B,那么A和B对彼此来说是原子的。

    通过锁,锁机制可以实现原子操作,但锁一般是阻塞的如synchronize关键字就是基于阻塞的锁机制,当一个线程拥有锁时,访问同一资源的其他线程就需要等待,直到该线程释放锁。

    但锁操作有着诸多的问题,如被阻塞的线程有限度比较高;获取锁的线程出现不释放情况;大量线程竞争锁,CPU会花费大量的时间和资源进行处理,此外锁机制是一种比较粗粒度的机制,对于像计数器这样的需求显得过于笨重。

    实现原子操作还可以通过现代处理器基本都支持的CAS()指令来完成。每一个CAS操作过程都包含三个运算符:一个内存地址V,一个期望的值A和一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作

    CAS的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少。循环CAS就是在一个循环里不断的做cas操作,直到成功为止。

    2.CAS实现原子操作的三大问题

    2.1 ABA问题

    因为CAS需要在操作值的时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

    ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A。

    2.2 循环时间长开销大

    自旋CAS如果长时间不成功,就会给CPU带来很大的执行开销。

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

    当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。

    还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如,有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。

    3.原子操作类的使用

    AtomicInteger  原子更新基本类型

    compareAndSet():先判断当前值(旧值)与期望值(expect)是否相等,如果相等 将新值(update)设置为当前值(覆盖了旧值),

    /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * @param expect the expected value
         * @param update the new value
         * @return {@code true} if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }


    /**
    * Gets the current value.
    *
    * @return the current value
    */
    public final int get() {
    return value;
    }
    incrementAndGet() (类比于) --> ++i
    getAndIncrement() (类比于)--> i++

    AtomicIntegerArray 原子更新数组里的整形

    原子更新引用类型:

    AtomicReference

    AtomicStampedReference 利用版本戳的形式记录每次改变后的版本号,解决ABA问题, pair使用 int stamp作为计数器使用

    AtomicMarkableReference 原子更新带有标记为的引用类型 pair使用boolean mark.关注是否被动过。

    参考:http://enjoy.ke.qq.com

  • 相关阅读:
    B2. Cat Party (Hard Edition)
    Dubbo集群容错
    Dubbo负载均衡
    Dubbo多协议支持
    Dubbo服务分组
    Dubbo多版本控制
    Dubbo管控平台
    Dubbo应用到web工程
    Dubbo使用Zookeeper注册中心
    Dubbo直连方式
  • 原文地址:https://www.cnblogs.com/cangshublogs/p/10763844.html
Copyright © 2020-2023  润新知