part7-原子操作CAS

目录页:https://blog.csdn.net/u011294519/article/details/88367808

1. Compare and Swap [CAS]   

1.1. 小声哔哔

    之前学习的synchronized本质上是一种悲观锁,虽然的确安全有效,但是却牺牲了性能,原因大家也能想明白,等待锁的线程啥也干不了,直到获取锁。这也是HashTable不被待见的原因,翻看源码会发现一堆的synchronized(扯远了)。

    那么问题来了,要啥自行车。Java为我们提供了CAS算法,一种乐观锁,我们希望在不受干扰的情况下进行更新操作,但是事实往往会有本山叔来要我们的自行车,CAS这种方法会进行冲突检测来判断更新期间是否有其他的干扰,以防被忽悠瘸了,在冲突检测中,如果发现操作失败我们可以选择重试或不重试,嗯,自行车保住了。

    CAS算法将内存位置的内容与给定值进行比较,只有当它们相同时,才将该内存位置的内容修改为给定的新值。这是作为单个原子操作完成的。原子性保证了新值的计算是基于最新的信息;如果该值已由另一个线程同时更新,则写入将失败。操作结果必须表明是否执行了替换;这可以通过一个简单的布尔响应(这种变体通常称为比较和设置)来实现,也可以通过返回从内存位置读取的值(而不是写入其中的值)来实现。(我承认这段是抄的)。其实CAS是依赖于现在CPU的指令操作。

    CAS的原子操作类包括:

    更新基本数据类型在java.util.concurrent.atomic包下以Atomic开头的类。

    下面只写几个示例代码

1.2. 更新基本类型:AtomicInteger

1.2.1. 主要方法

    getAndIncrement():获取值后加一

    getAndDecrement():获取值后减一

    incrementAndGet():加一后获取值

    decrementAndGet():减一后获取值

    get():获取值

    compareAndSet(int expect, int update):设置期望值之前比对更新值是否与内存中的值相同,若不相同返回false,若相同设置值并返回true

1.2.2. 上代码

package com.concurrent.coline.part7;

 

import java.util.concurrent.atomic.AtomicInteger;

 

/**

 *类说明:AtomicInt

 */

public class UseAtomicInt {

      

       static AtomicInteger ai = new AtomicInteger(10);

      

    public static void main(String[] args) {

        //10--->11

           System.out.println(ai.getAndIncrement());

           //11-->10

           System.out.println(ai.getAndDecrement());

        //10--->11

           System.out.println(ai.incrementAndGet());

           //11-->10

        System.out.println(ai.decrementAndGet());

        System.out.println(ai.get());

        System.out.println(ai.compareAndSet(11,10));

        System.out.println(ai.compareAndSet(10,10));

    }

}

运行结果:

   代码位置:compare-swap模块part7

1.3. 更新引用类型:AtomicReference

1.3.1. 主要方法

    compareAndSet(int expect, int update):设置期望值之前比对更新值是否与内存中的值相同,若不相同返回false,若相同设置值 并返回true

1.3.2. 上代码

package com.concurrent.coline.part7;

 

import java.util.concurrent.atomic.AtomicReference;

 

/**

 *类说明:演示引用类型的原子操作类

 */

public class UseAtomicReference {

      

       private static AtomicReference<UserInfo> userRef = new AtomicReference<UserInfo>();

      

    public static void main(String[] args) {

        //要修改的实体的实例

        UserInfo user = new UserInfo("Tom", 15);

        userRef.set(user);

 

        //要变化的新实例

        UserInfo updateUser = new UserInfo("Coline", 17);

        userRef.compareAndSet(user, updateUser);

 

        System.out.println(userRef.get().getName());

        System.out.println(userRef.get().getAge());

        System.out.println(user.getName());

        System.out.println(user.getAge());       

    }

   

    //定义一个实体类

    private static class UserInfo {

        private String name;

        private int age;

        private UserInfo(String name, int age) {

            this.name = name;

            this.age = age;

        }

        private String getName() {

            return name;

        }

        private int getAge() {

            return age;

        }

    }

 

}

运行结果:

    可以看到这里的更新并不会修改user的实例

    代码位置:compare-swap模块part7

1.4. 带版本戳更新:AtomicStampedReference

1.4.1. 说明

    这个类是为了解决ABA问题而存在的,比如alpha线程想把A->B,但是beta线程做了A->B->A,在alpha线程来做操作的时候无法判断A被beta线程变过,也许我们就存在这样的业务场景,不允许alpha再进行业务修改,而是提醒我们(我想到的是类似于秘钥修改这种安全相关的场景),这时候将修改对象与版本相关联就成为了一种很好的解决方案。

1.4.2. 上代码

package com.concurrent.coline.part7;

 

import java.util.concurrent.atomic.AtomicStampedReference;

 

/**

 * 类说明:演示带版本戳的原子操作类

 */

public class UseAtomicStampedReference {

 

    private static AtomicStampedReference<String> asr =

            new AtomicStampedReference<>("Coline", 0);

 

 

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

        //获取初始的版本号

        final int oldStamp = asr.getStamp();

        final String oldReferenc = asr.getReference();

 

        Thread rightStampThread = new Thread(() -> {

 

            System.out.println(Thread.currentThread().getName()

                    + "当前变量值:" + oldReferenc + "计划修改时版本戳:" + oldStamp + "-"

                    + asr.compareAndSet(oldReferenc, oldReferenc + "Java",

                    oldStamp, oldStamp + 1));

 

 

        });

 

        Thread errorStampThread = new Thread(() -> {

 

            System.out.println(Thread.currentThread().getName()

                    + "当前变量值:" + oldReferenc + "计划修改时版本戳:" + oldStamp + "-"

                    + asr.compareAndSet(oldReferenc, oldReferenc + "Java",

                    oldStamp, oldStamp + 1));

 

 

        });

 

        rightStampThread.start();

        rightStampThread.join();

        errorStampThread.start();

        errorStampThread.join();

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

 

    }

}

运行结果:

代码位置:compare-swap模块part7

1.5. 修改数组:AtomicIntegerArray

    比较简单,直接上代码:

package com.concurrent.coline.part7;

 

import java.util.concurrent.atomic.AtomicIntegerArray;

 

 

/**

 *类说明:AtomicArray

 */

public class AtomicArray {

    static int[] value = new int[] { 1, 2 };

   

    static AtomicIntegerArray ai = new AtomicIntegerArray(value);

   

    public static void main(String[] args) {

           ai.getAndSet(0, 3);

           System.out.println(ai.get(0));

           System.out.println(value[0]);

 

    }

}

运行结果:

代码位置:compare-swap模块part7

猜你喜欢

转载自blog.csdn.net/u011294519/article/details/88432052