IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> CAS都不了解,你还怎么看J-U-C,kafka入门教程 -> 正文阅读

[大数据]CAS都不了解,你还怎么看J-U-C,kafka入门教程

 * @return 返回未更新前的值
 */
 public final int getAndAddInt(Object var1, long var2, int var4) {
    //期待值
    int var5;
    do {
        //获取valueOffset对应的value的值,支持volatile load
        var5 = this.getIntVolatile(var1, var2);
        //如果原子更新失败,则一直重试,直到成功。
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

**我们看到CAS只能原子的更新一个值,如果我们要原子更新多个值,CAS可以做到吗?**答案是可以的。

#### 2.4、AtomicReference

如果要原子地更新多个值,就需要使用**AtomicReference**。其使用的是**compareAndSwapObject**方法。可以将多个值封装到一个对象中,原子地更换对象来实现原子更新多个值。

public class MultiValue {
private int value1;
private long value2;
private Integer value3;

public MultiValue(int value1, long value2, Integer value3) {
    this.value1 = value1;
    this.value2 = value2;
    this.value3 = value3;
}

}

public class AtomicReferenceTest {
public static void main(String[] args) {
MultiValue multiValue1 = new MultiValue(1, 1, 1);
MultiValue multiValue2 = new MultiValue(2, 2, 2);
MultiValue multiValue3 = new MultiValue(3, 3, 3);
AtomicReference atomicReference = new AtomicReference<>();
//因为构造AtomicReference时,没有使用有参构造函数,所以value默认值是null
atomicReference.compareAndSet(null, multiValue1);
System.out.println(atomicReference.get());
atomicReference.compareAndSet(multiValue1, multiValue2);
System.out.println(atomicReference.get());
atomicReference.compareAndSet(multiValue2, multiValue3);
System.out.println(atomicReference.get());
}
}
//输出结果
//MultiValue{value1=1, value2=1, value3=1}
//MultiValue{value1=2, value2=2, value3=2}
//MultiValue{value1=3, value2=3, value3=3}


我们再看一看AtomicReference的compareAndSet方法。

注意:**这里的比较都是使用==而非equals方法**。所以最好封装的MultiValue不要提供set方法。

public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}


#### 2.5、CAS的ABA问题

**假设你的账户上有100块钱,你要给女票转50块钱。**

**我们使用CAS进行原子更新账户余额。由于某种原因,你第一次点击转账出现错误,你以为没有发起转账请求,这时候你又点击了一次。系统开启了两个线程进行转账操作,第一个线程进行CAS比较,发现你的账户上预期是100块钱,实际也有100块钱,这时候转走了50,需要设置为100 - 50 = 50 元,这时账户余额为50**

**第一个线程操作成功了,第二个线程由于某种原因阻塞住了;这时候,你的家人又给你转了50块钱,并且转账成功。那你账户上现在又是100块钱;**

**太巧了,第二个线程被唤醒了,发现你的账户是100块钱,跟预期的100是相等的,这时候又CAS为50。大兄弟,哭惨了,你算算,正确的场景你要有多少钱?**这就是CAS存在的ABA问题。

public class AtomicIntegerABA {

private static AtomicInteger atomicInteger = new AtomicInteger(100);

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(3);

    //线程1
    executorService.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
        atomicInteger.compareAndSet(100, 50);
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
    });

    //线程2
    executorService.execute(() -> {
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
        atomicInteger.compareAndSet(50, 100);
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
    });

    //线程3
    executorService.execute(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
        atomicInteger.compareAndSet(100, 50);
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get());
    });

    executorService.shutdown();
}

}
//输出结果
//pool-1-thread-1 - 100
//pool-1-thread-1 - 50
//pool-1-thread-2 - 50
//pool-1-thread-2 - 100
//pool-1-thread-3 - 100
//pool-1-thread-3 - 50


大家心想,靠,这不是坑吗?那还用。。。。。。。。。。。。。。冷静,冷静。你能想到的问题,jdk都能想到。atomic包提供了一个**AtomicStampedReference**

#### 2.6、AtomicStampedReference

看名字是不是跟AtomicReference很像啊,其实就是在AtomicReference上**加上了一个版本号,每次操作都对版本号进行自增,那每次CAS不仅要比较value,还要比较stamp,当且仅当两者都相等,才能够进行更新。**

public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
//定义了内部静态内部类Pair,将构造函数初始化的值与版本号构造一个Pair对象。
private static class Pair {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static Pair of(T reference, int stamp) {
return new Pair(reference, stamp);
}
}

//所以我们之前的value就对应为现在的pair
private volatile Pair pair;


让我们来看一看它的CAS方法。

public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair current = pair;
return
//只有在旧值与旧版本号都相同的时候才会更新为新值,新版本号
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair cmp, Pair val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}


还是上面转账的例子,我们使用AtomicStampedReference来看看是否解决了呢。

public class AtomicStampedReferenceABA {
/**
* 初始化账户中有100块钱,版本号对应0
*/
private static AtomicStampedReference atomicInteger = new AtomicStampedReference<>(100, 0);

public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    int[] result = new int[1];
    //线程1
    executorService.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
        //将100更新为50,版本号+1
        atomicInteger.compareAndSet(100, 50, 0, 1);
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
    });

    //线程2
    executorService.execute(() -> {
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
        //将50更新为100,版本号+1
        atomicInteger.compareAndSet(50, 100, 1, 2);
        System.out.println(Thread.currentThread().getName() + " - " + atomicInteger.get(result));
    });

    //线程3
    executorService.execute(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);

最后

由于细节内容实在太多了,为了不影响文章的观赏性,只截出了一部分知识点大致的介绍一下,每个小节点里面都有更细化的内容!

需要这份文档的朋友可以帮忙点个赞,点击下方神秘超链接,就可以免费获取到了,还有小编准备的一份Java进阶学习路线图(Xmind)以及来年金三银四必备的一份《Java面试必备指南》

资料领取链接:Java进阶学习路线图(Xmind)+《Java面试必备指南》

mg-KT2JnPE3-1630204782148)]

需要这份文档的朋友可以帮忙点个赞,点击下方神秘超链接,就可以免费获取到了,还有小编准备的一份Java进阶学习路线图(Xmind)以及来年金三银四必备的一份《Java面试必备指南》

资料领取链接:Java进阶学习路线图(Xmind)+《Java面试必备指南》

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-08-30 12:07:06  更:2021-08-30 12:09:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:55:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码