Java并发编程学习(7)---原子操作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);
        System.out.println(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值