CAS 原子操作类

news2024/12/22 22:52:55

CAS

原子类 java.util.concurrent.atomic

是什么

CAS

compare and swap的缩写,中文翻译比较并交换,实现并发算法时常用的一种技术

它包含三个操作数–内存位置、预期原值及更新值

执行CAS操作时,将内存位置的值与预期原值比较

如果相匹配,那么处理器会自动将该位置的值更新为新值;

如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功

硬件级别保证

CAS是JDK提供的非阻塞原子性操作,他通过硬件保证了比较-更新的原子性

他是非阻塞的且自身具有原子性,也就是说这玩意效率更高且通过硬件保证,说明这玩意更可靠

CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe提供的CAS方法(如compareAndSwapXXX)底层实现即为CPU指令cmpxchg

执行cmpxch指令的时候,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一个线程会对总线加锁成功,加锁成功之后会执行cas操作,也就是说CAS的原子性实际上是CPU实现独占的,比起synchronized重量级锁,这里的排他时间短很多,所以在多线程情况下性能会比较好
请添加图片描述

i++线程不安全,那atomicInteger.getAndIncrement()

AtomicInteger类主要利用CAS(compare and swap)+ volatile 和 native 方法来保证原子操作,从而避免synchronized的高开销,执行效率大为提升

CAS并发原语体现在JAVA语言中的就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统原语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题

CAS 总结

只需要记住:CAS是靠硬件实现的从而在硬件层提升效率,底层还是交给硬件来保证原子性和可见性

实现方式是基于硬件平台的汇编指令,在intel的CPU中(X86机器上,使用的是汇编指令cmpxchg)指令

核心思想是:比较要更新变量的值V和预期值E(compare),相等才会将V的值设为新值N(swap),如果不相等自旋再来

CAS与自旋锁,借鉴CAS思想

是什么

CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,已达到锁的效果,至于自旋,字面意思,自己旋转。是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,当线程发现锁被占用是,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗cpu

​ OpenJDK源码里面查看下Unsafe.java

CAS是实现自旋锁的基础,自旋锁翻译成人话就是循环,一般是用一个无限循环实现。这样一来,一个无限循环中,执行一个CAS操作

当操作成功返回true时,循环结束

当返回false时,接着执行循环,继续尝试CAS操作,直到返回true

自旋锁

/**
 * 题目:实现一个自旋锁,
 * 自旋锁好处:循环比较获取没有类似wait的阻塞
 * 
 * 通过CAS操作完成自旋锁,A线程先进来调用lock方法持有锁5秒钟,B随后进来发现当前有线程持有锁,所以只能通过自旋等待,直到A释放锁后B随后抢到
 */
public class SpinLock {
    private AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t" + "----- come in");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    public void unLock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t" + "----- task over ,unlock");
    }

    public static void main(String[] args) {
        SpinLock spinLock = new SpinLock();
        new Thread(() -> {
            spinLock.lock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            spinLock.unLock();
        },"a").start();

        // 保证a 先启动
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            spinLock.lock();

            spinLock.unLock();
        },"b").start();
    }
}

CAS 缺点

  1. 循环时间长开销大
  2. ABA问题 解决 -》版本号 使用AtomicSatmpedReference

ABA问题解决演示

@Getter
@Data
@AllArgsConstructor
class User{
    String name;
    int age;
}
public class Thread3 {
    public static void main(String[] args) {
        User u3 = new User("张三", 23);
        AtomicStampedReference<User> stampedReference = new AtomicStampedReference<>(u3, 1);
        System.out.println(stampedReference.getReference() + " \t " + stampedReference.getStamp());
        User u4 = new User("李四", 23);
        stampedReference.compareAndSet(u3,u4,stampedReference.getStamp(),stampedReference.getStamp()+1);
        System.out.println(stampedReference.getReference() + " \t " + stampedReference.getStamp());
        stampedReference.compareAndSet(u4,u3,stampedReference.getStamp(),stampedReference.getStamp()+1);
        System.out.println(stampedReference.getReference() + " \t " + stampedReference.getStamp());
    }
}

原子操作类之18罗汉增强

请添加图片描述
请添加图片描述

基本类型原子类

AtomicInteger

AtomicBoolean

AtomicLong

class AtomicDemo {
    public AtomicInteger atomicInteger = new AtomicInteger();

    public void add() {
        atomicInteger.getAndAdd(1);
    }

}

public class Thread3 {
    public static Integer SIZE = 50;
    public static void main(String[] args) throws InterruptedException {
        AtomicDemo ad = new AtomicDemo();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (Integer i = 0; i < SIZE; i++) {
            new Thread(() -> {
              try {
                  for (int j = 0; j < 1000; j++) {
                      ad.add();
                  }
              }finally {
                  countDownLatch.countDown();
              }
            }).start();
        }
        countDownLatch.await();
        System.out.println(ad.atomicInteger.get());

    }
}

数组类型原子类

AtomicIntegerArray

AtomicLongArray

AtomicReferenceArray

AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(3);
AtomicIntegerArray atomicIntegerArray1 = new AtomicIntegerArray(new int[]{1, 2, 3, 4});

引用类型原子类

AtomicReference

AtomcStampedReference(多次)

AtomicMarkableReference(一次性)

对象的属性修改原子类

AtomicIntegerFieldUpdater

AtomicLongFieldUpdater

AtomicReferenceFieldUpdater

使用

​ 更新的对象属性必须使用public volatile修饰符

因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置要更新的类和属性

面试:哪里用到了volatile?单例模式,AtomicReferenceFieldUpdater

class BankAccount{
    public volatile int money = 0;

    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");

    public void transMoney(BankAccount bankAccount)
    {
        fieldUpdater.getAndIncrement(bankAccount);
    }
}

public class Thread3 {
    public static Integer SIZE = 50;
    public static void main(String[] args) throws InterruptedException {
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (Integer i = 0; i < SIZE; i++) {
            new Thread(() -> {
               try {
                   for (int j = 0; j < 1000; j++) {
                       bankAccount.transMoney(bankAccount);
                   }
               }finally {
                   countDownLatch.countDown();
               }
            }).start();
        }
        countDownLatch.await();

        System.out.println(bankAccount.money);
    }
}
/**
 * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,要求只能被初始化一次,只有一个线程操作成功
 */
class MyVar{
    public volatile Boolean isInit = Boolean.FALSE;

    AtomicReferenceFieldUpdater<MyVar, Boolean> referenceFieldUpdater =
            AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");

    public void init(MyVar myVar)
    {
        if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)) {
            System.out.println(Thread.currentThread().getName()+ "  start init,need 2 seconds");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+ "  init over");
        }else{
            System.out.println(Thread.currentThread().getName()+ " 已被初始化");
        }
    }
}
public class Thread3 {
    public static void main(String[] args) {
        MyVar myVar = new MyVar();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                myVar.init(myVar);
            },""+i).start();
        }
    }
}

原子操作增强类原理深度解析

DoubleAccumulator

DoubleAdder

LongAccumulator

LongAddr

请添加图片描述
LongAddr累加操作

LongAccumulator 自定义连续操作

LongAccumulator longAccumulator = new LongAccumulator((left, right) -> left + right, 0);
longAccumulator.accumulate(1);
System.out.println(longAccumulator.get()); 
longAccumulator.accumulate(3);
System.out.println(longAccumulator.get());

性能测试

class ClickNumber {
    int number = 0;
    public synchronized void clickBySync()
    {
        number++;
    }

    AtomicLong atomicLong = new AtomicLong(0);
    public void clickByAtomicLong(){
        atomicLong.getAndIncrement();
    }

    LongAdder longAdder = new LongAdder();
    public void clickLongAdder()
    {
        longAdder.increment();
    }

    LongAccumulator longAccumulator = new LongAccumulator((LongBinaryOperator) (left, right) -> left+right,0);
    public void clickLongAcc()
    {
        longAccumulator.accumulate(1);
    }
}

/**
 * 50个线程,每个线程100w次,总点赞数出来
 */
public class Thread3 {
    public static final int _1W = 10000;

    public static final int threadNumber = 500;
    public static void main(String[] args) throws InterruptedException {

        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;


        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);

        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _1W; j++) {
                        clickNumber.clickBySync();
                    }

                }finally {
                    countDownLatch1.countDown();
                }
            }).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("sync "+(endTime-startTime));


        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNumber; i++) {
            new Thread(() -> {
                try {

                    for (int j = 0; j < _1W; j++) {
                        clickNumber.clickByAtomicLong();
                    }

                }finally {
                    countDownLatch2.countDown();
                }
            }).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("ato "+(endTime-startTime));


        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _1W; j++) {
                        clickNumber.clickLongAdder();
                    }


                }finally {
                    countDownLatch3.countDown();
                }
            }).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("addr "+(endTime-startTime));


        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNumber; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _1W; j++) {
                        clickNumber.clickLongAcc();
                    }

                }finally {
                    countDownLatch4.countDown();
                }
            }).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("acc "+(endTime-startTime));
    }
}

请添加图片描述

请添加图片描述
LongAdder 为什这么快?

base变量:低并发,直接累加到该变量上

Cell[]:高并发,累加进各个线程自己的槽Cell[i]中

请添加图片描述

总结

AtomicLong

原理

​ CAS+自旋,incrementAndGet

场景

​ 低并发下的全局计算

​ AtomicLong能保证并发情况下计数的准确性,通过CAS来解决并发问题

缺陷

​ 高并发后性能急剧下降

​ why? AtomicLong 自旋锁会成为瓶颈


LongAdder

原理

​ CAS+Base+Cell数组分散

​ 空间换时间并分撒了热点数据

场景

​ 高并发下全局计算

缺陷

​ 计算结果不是实时性的,可以保证最终一致性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/493388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络协议与攻击模拟-05-ICMP协议

ICMP 协议 1、理解 ICMP 协议 2、理解 ICMP 重定向 3、会使用 wireshark 分析 ICMP 重定向流量实验 一、 ICMP 基本概念 1、 ICMP 协议 Internet 控制报文协议&#xff0c;用于在 IP 主机、路由器之间传递控制消息&#xff0c;控制消息指网络通不通、主机是否可达、路由是否…

荔枝派Zero(全志V3S)驱动开发之hello驱动程序

文章目录 前言一、设备驱动分类二、字符设备驱动简介三、字符设备驱动开发1、APP打开的文件在内核中如何表示2、编写驱动程序的步骤3、hello 驱动程序编写<1>、试验程序编写<2>、测试程序编写<3>、编写 Makefile<4>、编译 3、运行测试<1>、上传程…

PyTorch实战4:猴痘病识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;365天深度学习训练营-第P4周&#xff1a;猴痘病识别&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 目录 一、搭建CNN网络结构1、原文网络结构1.1、网络…

4。计算机组成原理(2)存储系统

嵌入式软件开发&#xff0c;非科班专业必须掌握的基本计算机知识 核心知识点&#xff1a;数据表示和运算、存储系统、指令系统、总线系统、中央处理器、输入输出系统 这一部分主要讲解了CPU的组成和扩容、CPU与存储器&#xff08;主存、辅存、缓存&#xff09;的连接 一 存储…

C++笔记——第十六篇 异常

目录 1.C语言传统的处理错误的方式 2. C异常概念 3. 异常的使用 3.1 异常的抛出和捕获 在函数调用链中异常栈展开匹配原则 3.2异常安全 4.异常的优缺点 1.C语言传统的处理错误的方式 传统的错误处理机制&#xff1a; 1. 终止程序&#xff0c;如assert&#xff0c;缺陷&a…

飞腾ft2000-麒麟V10-SP1安装Docker、运行gitlab容器

目录 一、安装及配置docker 1、卸载docker相关包及删除相关配置文件 2、安装二进制docker 1.下载软件包 2.解压 3.修改镜像加速地址 4.修改profile文件 5.启动docker 6.docker常用命令 二、安装并启动gitlab镜像 1.安装gitlab镜像 1.查询满足使用需求的gitlab版本 2…

很佩服的一个Google大佬,离职了。。

这两天&#xff0c;科技圈又有一个突发的爆款新闻相信不少同学都已经看到了。 那就是75岁的计算机科学家Geoffrey Hinton从谷歌离职了&#xff0c;从而引起了科技界的广泛关注和讨论。 而Hinton自己也证实了这一消息。 提到Geoffrey Hinton这个名字&#xff0c;对于一些了解过…

使用 Mercury 直接从 Jupyter 构建 Web 程序

动动发财的小手&#xff0c;点个赞吧&#xff01; 有效的沟通在所有数据驱动的项目中都至关重要。数据专业人员通常需要将他们的发现和见解传达给利益相关者&#xff0c;包括业务领导、技术团队和其他数据科学家。 虽然传达数据见解的传统方法&#xff08;如 PowerPoint 演示文…

Oracle SQL优化相关数据项

要掌握SQL调优技术,就需要能读懂SQL语句的执行计划,要想读懂SQL语句的执行计划,不仅需要准确理解SQL语句执行计划中各操作及其含义,还需要准确理解SQL语句执行计划中各数据项的含义。本书第7章中,已经对SQL语句执行计划中各个操作的含义做了详尽的阐述,本章中,我们将对S…

爱普特APT32F110x系列时钟介绍

最近要用APT32F110x做一些开发&#xff0c;顺便学习一下。 APT32F110x 是由爱普特推出的基于平头哥&#xff08;T-Head Microsystems&#xff09;CPU 内核开发的 32 位高性能低成本单片机。 APT32F1104x基于嵌入式 Flash 工艺制造&#xff0c;内部丰富的模拟资源&#xff0c;包…

ShardingJDBC核心概念与快速实战

目录 ShardingSphere介绍 ShardingSphere特点 ShardingSphere简述 ShardingSphere产品区分 ShardingJDBC实战 核心概念 实战 ShardingJDBC的分片算法 ShardingSphere目前提供了一共五种分片策略&#xff1a; 分库分表带来的问题 ShardingSphere介绍 ShardingSphere特…

结合SSE实现实时位置展示与轨迹展示

概述 实时位置与实时轨迹的展示是webgis中非常常见的一个功能&#xff0c;本文结合SSE来实现实现此功能。 SSE简介 SSE是Sever-Sent Event的首字母缩写&#xff0c;它是基于HTTP协议的&#xff0c;在服务器和客户端之间打开一个单向通道&#xff0c;服务端响应的不再是一次性…

车牌输入框 封装 (小程序 vue)

车牌输入框 封装 小程序licenseNumber.jslicenseNumber.jsonlicenseNumber.wxmllicenseNumber.wxss样例 vuevnp-input-box.vuevnp-input.vuevnp-keyboard.vue样例 小程序 licenseNumber.js const INPUT_NUM 8;//车牌号输入框个数 const EmptyArray new Array(INPUT_NUM).fi…

6个「会议议程」实例和免费模板

我们都参加过一些团队会议&#xff0c;在这些会议上&#xff0c;大多数与会者对会议的目的一无所知&#xff0c;而发言者则使讨论偏离轨道。 接下来就是一场真正的灾难了。 你会发现你的团队因为“上述会议”而浪费了很多时间&#xff0c;却没有达到任何目的。 好消息! 一个…

【Python】序列类型②-元组

文章目录 1.元组简介2.元组的定义2.1定义只有一个元素的元组 3.元组的下标访问4.元组的常用方法5.使用in判断是否存在元素6.多元赋值操作 1.元组简介 元组和列表一样可以存放多个,不同数据类型的元素 与列表最大的不同就是:列表是可变的,而元组不可变 2.元组的定义 元组的定义:…

TCP/UDP协议

一、协议的概念 什么是协议&#xff1f; 从应用的角度出发&#xff0c;协议可理解为“规则”&#xff0c;是数据传输和数据的解释的规则。 假设&#xff0c;A、B双方欲传输文件。规定&#xff1a; 第一次&#xff0c;传输文件名&#xff0c;接收方接收到文件名&#xff0c;…

Springboot +Flowable,ReceiveTask的简单使用方法

一.简介 ReceiveTask&#xff08;接受任务&#xff09;&#xff0c;它的图标如下图所示&#xff1a; ReceiveTask 可以算是 Flowable 中最简单的一种任务&#xff0c;当该任务到达的时候&#xff0c;它不做任何逻辑&#xff0c;而是被动地等待用户确认。 ReceiveTask 往往适…

RepVGG: Making VGG-style ConvNets Great Again

文章地址&#xff1a;《RepVGG: Making VGG-style ConvNets Great Again》 代码地址&#xff1a;https://github.com/megvii-model/RepVGG 文章发表于CVPR2021&#xff0c;文章提出一种将训练态和推断态网络结构解耦的方法。文章认为目前复杂的网络结构能够获取更高的精度&am…

学大数据需要java学到什么程度

大数据需求越来越多&#xff0c;只有技术在手不愁找不到工作。 学习大数据需要掌握什么语言基础&#xff1f; 1、Java基础 大数据框架90%以上都是使用Java开发语言&#xff0c;所以如果要学习大数据技术&#xff0c;首先要掌握Java基础语法以及JavaEE方向的相关知识。 2、My…

记一次OJ在线代码编辑器(代码编译+运行,C、C++、Java)

如何在SpringBootVue的项目中实现在线代码编译及执行&#xff08;支持编译运行C、C、Java&#xff09;&#xff0c;研究了一天&#xff0c;真实能用&#xff0c;下面直接上源码&#xff01;&#xff01;&#xff01; ————————————————————————————…