8. 原子操作类

news2024/11/17 14:21:57

Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
8.1 基本类型原子类
● AtomicInteger:整型原子类
● AtomicBoolean:布尔型原子类
● AtomicLong:长整型原子类

8.1.1 常用API简介
基本类型原子类常用API简介

public final int get() //获取当前的值
public final int getAndSet(int newValue)//获取当前的值,并设置新的值
public final int getAndIncrement()//获取当前的值,并自增
public final int getAndDecrement() //获取当前的值,并自减
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

8.1.2 Case
AtomicInteger案例演示

class MyNumber {
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addPlusPlus() {
        atomicInteger.getAndIncrement();
    }

}

public class AtomicIntegerDemo {

    public static final int SIZE = 50;

    public static void main(String[] args) throws InterruptedException {
        MyNumber myNumber = new MyNumber();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (int i = 1; i <= SIZE; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 10; j++) {
                        myNumber.addPlusPlus();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            }, String.valueOf(i)).start();

        }
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName() + "\t" + "result: " + myNumber.atomicInteger.get());//main	result: 500
    }
}

8.2 数组类型原子类
AtomicIntegerArray:整型数组原子类
AtomicLongrArray:长整型数组原子类
AtomicReferenceArray:用类型数组原子类
8.2.1 常用API简介
数组类型原子类常用API简介

public final int get(int i) //获取 index=i 位置元素的值
public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
public final int getAndAdd(int i, int delta) //获取 index=i 位置元素的值,并加上预期的值
boolean compareAndSet(int i, int expect, int update) //如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update)
public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

8.2.2 Case
AtomicIntegerArray案例演示

public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
//        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
        for (int i = 0; i < atomicIntegerArray.length(); i++) {
            System.out.println(atomicIntegerArray.get(i));
        }
        System.out.println();
        int tempInt = 0;
        tempInt = atomicIntegerArray.getAndSet(0, 1122);
        System.out.println(tempInt + "\t" + atomicIntegerArray.get(0));
        tempInt = atomicIntegerArray.getAndIncrement(0);
        System.out.println(tempInt + "\t" + atomicIntegerArray.get(0));
    }
}
8.3 引用类型原子类
● AtomicReference :引用类型原子类
● AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
  ○ 解决修改过几次
● AtomicMarkableReference:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来
  ○ 解决是否修改过,它的定义就是将标记戳简化为true/false,类似于一次性筷子
public class AtomicMarkableReferenceDemo {
    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100, false);

    public static void main(String[] args) {
        new Thread(() -> {
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName() + "\t" + "默认标识: " + marked);//t1	默认标识: false
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            markableReference.compareAndSet(100, 1000, marked, !marked);//t2	默认标识: false

        }, "t1").start();

        new Thread(() -> {
            boolean marked = markableReference.isMarked();
            System.out.println(Thread.currentThread().getName() + "\t" + "默认标识: " + marked);//t2	t2线程CASResult:false
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
            System.out.println(Thread.currentThread().getName() + "\t" + "t2线程CASResult:" + b);
            System.out.println(Thread.currentThread().getName() + "\t" + markableReference.isMarked());//t2	true
            System.out.println(Thread.currentThread().getName() + "\t" + markableReference.getReference());//t2	1000

        }, "t2").start();
    }
}

8.4 对象的属性修改原子类
● AtomicIntegerFieldUpdater:原子更新对象中int类型字段的值
● AtomicLongFieldUpdater:原子更新对象中Long类型字段的值
● AtomicReferenceFieldUpdater:原子更新对象中引用类型字段的值

8.4.1 使用目的
以一种线程安全的方式操作非线程安全对象内的某些字段

在这里插入图片描述
8.4.2 使用要求
● 更新的对象属性必须使用public volatile修饰符
● 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性

class BankAccount {
    public volatile int money = 0;


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

    public void transferMoney(BankAccount bankAccount) {
        atomicIntegerFieldUpdater.getAndIncrement(bankAccount);

    }
}

public class AtomicIntegerFieldUpdaterDemo {
    public static void main(String[] args) throws InterruptedException {
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 1000; j++) {
                        bankAccount.transferMoney(bankAccount);
                    }
                } finally {
                    countDownLatch.countDown();
                }
            }, String.valueOf(i)).start();

        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + '\t' + "result: " + bankAccount.money); //main	result: 10000
    }
}
/**
 * 需求:多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作
 * 要求只能被初始化一次,只有一个线程操作成功
 */
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() + "\t" + "--------------start init ,need 2 secondes");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t" + "--------------over init");
        } else {
            System.out.println(Thread.currentThread().getName() + "\t" + "--------------已经有线程进行初始化工作了。。。。。");
        }
    }
}

public class AtomicReferenceFieldUpdaterDemo {

    public static void main(String[] args) {
        MyVar myVar = new MyVar();
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                myVar.init(myVar);
            }, String.valueOf(i)).start();
        }
    }
}
/**
 * 1	--------------start init ,need 2 secondes
 * 5	--------------已经有线程进行初始化工作了。。。。。
 * 2	--------------已经有线程进行初始化工作了。。。。。
 * 4	--------------已经有线程进行初始化工作了。。。。。
 * 3	--------------已经有线程进行初始化工作了。。。。。
 * 1	--------------over init
 */

8.5 原子操作增强类原理深度解析
● DoubleAccumulator:一个或多个变量,它们一起保持运行double使用所提供的功能更新值
● DoubleAdder:一个或多个变量一起保持初始为零double总和
● LongAccumulator:一个或多个变量,一起保持使用提供的功能更新运行的值long ,提供了自定义的函数操作
● LongAdder:一个或多个变量一起维持初始为零long总和(重点),只能用来计算加法,且从0开始计算

8.5.1 常用API
在这里插入图片描述
8.5.2 面试题
热点商品点赞计算器,点赞数加加统计,不要求实时精确
一个很大的list,里面都是int类型,如何实现加加,思路?
8.5.3 点赞计数器
多种方式实现点赞计数器案例演示结果

class ClickNumber {
    int number = 0;

    public synchronized void clickBySynchronized() {
        number++;
    }

    AtomicLong atomicLong = new AtomicLong(0);

    public void clickByAtomicLong() {
        atomicLong.getAndIncrement();
    }

    LongAdder longAdder = new LongAdder();

    public void clickByLongAdder() {
        longAdder.increment();
    }

    LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);

    public void clickByLongAccumulator() {
        longAccumulator.accumulate(1);
    }
}

public class AccumulatorCompareDemo {
    public static final int _1W = 10000;
    public static final int THREAD_NUMBER = 50;

    public static void main(String[] args) throws InterruptedException {
        ClickNumber clickNumber = new ClickNumber();
        long StartTime;
        long endTime;
        CountDownLatch countDownLatch1 = new CountDownLatch(THREAD_NUMBER);
        CountDownLatch countDownLatch2 = new CountDownLatch(THREAD_NUMBER);
        CountDownLatch countDownLatch3 = new CountDownLatch(THREAD_NUMBER);
        CountDownLatch countDownLatch4 = new CountDownLatch(THREAD_NUMBER);

        StartTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        clickNumber.clickBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("------costTime: " + (endTime - StartTime) + " 毫秒" + "\t clickBySynchronized: " + clickNumber.number);

        StartTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        clickNumber.clickByAtomicLong();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("------costTime: " + (endTime - StartTime) + " 毫秒" + "\t clickByAtomicLong: " + clickNumber.atomicLong.get());

        StartTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        clickNumber.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("------costTime: " + (endTime - StartTime) + " 毫秒" + "\t clickByLongAdder: " + clickNumber.longAdder.sum());

        StartTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * _1W; j++) {
                        clickNumber.clickByLongAccumulator();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("------costTime: " + (endTime - StartTime) + " 毫秒" + "\t clickByLongAccumulator: " + clickNumber.longAccumulator.get());

    }
}
/**
 * ------costTime: 1313 毫秒	 clickBySynchronized: 50000000
 * ------costTime: 825 毫秒	 clickByAtomicLong: 50000000
 * ------costTime: 92 毫秒	 clickByLongAdder: 50000000
 * ------costTime: 61 毫秒	 clickByLongAccumulator: 50000000
 */

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

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

相关文章

SIMD加速矩阵运算

一、SIMD指令简介 SIMD的全称叫做&#xff0c;单指令集多数据&#xff08;Single Instruction Multiple Data&#xff09;。最直观的理解就是&#xff0c;向量计算。比如一个加法指令周期只能算一组数&#xff08;一维向量相加&#xff09;&#xff0c;使用SIMD的话&#xff0…

应届生求职个人简历模板(合集)

应届生求职个人简历模板1 基本信息 姓 名&#xff1a; 性别&#xff1a; 婚姻状况&#xff1a; 民族&#xff1a; 户 籍&#xff1a; 年龄&#xff1a; 现所在地&#xff1a; 身高&#xff1a; 联系电话&#xff1a; 电子邮箱&#xff1a;__ 求职意向 希望岗位&#xff1a;Web前…

【建议】强烈推荐ES6函数自由传参的写法,针对方法体的可扩展性很有帮助

let fun({a,b,c}{a:1,b:2,c:3})>{console.log(a);console.log(b);console.log(c); } 接下来我们即可自由传参&#xff0c;需要什么就传递什么 以上写法非常适用于需求变更的情况下自由传递参数&#xff0c;而且形参的数量、传参先后顺序可以根据业务自由搭配&#xff0c;非常…

Feign接口windows启动调用正常,Linux环境调用404

1、Linux环境启动之后报错 如下&#xff1a; windows 是调用正常得 反复测试好几轮 还是这样 &#xff0c;nacos都是注册进去得 helper-service 调用 xTIMS-Web 解决&#xff1a;FeignClient注解 不配置URL会出现那样问题&#xff0c; 配置URL之后 解决 &#xff0c;不报错了…

延迟队列--DelayQueue(JDK)

JDK自身支持延迟队列的数据结构&#xff0c;其实类&#xff1a;java.util.concurrent.DelayQueue。 我们通过阅读源码的方式理解该延迟队列类的实现过程。 1.定义 DelayQueue:是一种支持延时获取元素的无界阻塞队列。 特性&#xff1a; 线程安全&#xff1b; 内部元素有“…

人工智能算力需求稳增,中国将持续夯实算力底座

中国始终强调科技兴国的重要性。数字经济时代&#xff0c;技术的力量更为凸显。近年来&#xff0c;中国政府相关部门相继发布一系列政策&#xff0c;更加明确了人工智能对于提升中国核心竞争力的重要支撑作用&#xff0c;加上新基建、数字经济等持续利好政策的推动&#xff0c;…

0202性能分析-索引-MySQL

1 索引语法 创建索引 CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(index_column_name,...);Index_name&#xff1a;规范为idx_表名_字段名... 查看索引 SHOW INDEX FROM table_name;删除索引 DROP INDEX index_name ON table_name;按照下列要求&#xff0c;创建…

仿交易猫链接 跳转APP功能

最新仿交易猫假链接&#xff0c;带有跳转APP功能 下载程序&#xff1a;https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3

C语言数据结构——循环链表

如果人生会有很长,愿有你的荣耀永不散场。——《全职高手》 一 . 循环单链表 循环单链表是单链表的另一种形式&#xff0c;其结构特点是&#xff0c;链表中最后一个结点的指针域不再是结束标记&#xff0c;而是指向整个链表的第一个结点&#xff0c;从而使链表形成一个环。 和单…

PLC现场安装和维护的注意事项

虽然PLC是专门在现场使用的控制装置&#xff0c;在设计制造时已采取了很多措施&#xff0c;使它对工业环境比较适应&#xff0c;但是为了确保整个系统稳定可靠&#xff0c;还是应当尽量使PLC有良好的工作环境条件&#xff0c; 并采取必要的抗干扰措施。因此&#xff0c;PLC在安…

python中,unicode对象怎么转换成dict?

python中&#xff0c;unicode对象怎么转换成dict&#xff1f; 使用loads两次

「展会前线」易天光通信盛装亮相2023越南通讯展会

2023年6月7日&#xff0c;在历经了忙碌有序的前期准备工作后&#xff0c;易天光通信销售团队带着满满的信心踏上了越南通讯展会之旅&#xff01; “千呼万唤始出来&#xff0c;犹抱琵琶半遮面”。2023年6月8日&#xff0c;各方期待已久的2023越南通讯展会在越南胡志明市正式开…

肠道有害菌属——假单胞菌属(Pseudomonas),多变且适应性强

谷禾健康 假单胞菌属&#xff08;Pseudomonas&#xff09;是最多样化和普遍存在的细菌属之一&#xff0c;其物种存在于沉积物、临床样本、植物&#xff08;或植物根际&#xff09;、患病动物、水、土壤、海洋、沙漠等&#xff0c;这反映在它们多变的代谢能力和广泛的适应环境的…

3款好用的客户系统管理软件推荐,你用过哪款?

进行客户资料管理确实很重要。我本人在工作中也常常遇到客户关系管理的难题&#xff0c;有时候忘记填写客户信息&#xff0c;亦或是填错信息等场景&#xff0c;甚至会造成许多尴尬局面。为了解决这个问题&#xff0c;我也试用了很多个方法来提高效率。下面我想谈一谈我本人在摸…

十肽-4/Decapeptide-10, CG-IDP2——有效逆转皮肤衰老

简介----十肽-4 十肽-4可以穿透真皮增加胶原蛋白&#xff0c;通过从内至外的重建来逆转皮肤老化的过程&#xff1b;刺激胶原蛋白、弹力纤维和透明质酸增生&#xff0c;提高肌肤的含水量和锁水度&#xff0c;增加皮肤厚度以及减少细纹。 功效与应用----十肽-4 抗皱抗衰老 改善…

浪潮 KaiwuDB x 大数据中心 | 数据驱动政府治理能力快速提升

业务背景 我国工业互联网大数据资源存在孤立、分散、封闭等问题&#xff0c;数据价值未能得到有效利用&#xff0c;数据主权和数据安全面临重大威胁。 发挥数据对工业经济的基础支撑和创新引擎作用&#xff0c;可促进工业互联网的创新发展&#xff0c;加速数据驱动政府治理能…

Pycharm中的find usages有什么用?

问题描述&#xff1a;我们经常使用Pycharm作为开发工具&#xff0c;我们右键会发现有个find usages功能。 比如&#xff0c;我们以YOLOv8中的detect/train.py中的DetectionTrainer()类为例&#xff0c;右键之后如下图所示。 答案&#xff1a;全局搜索&#xff0c;查找类、变量…

「最新」Parallels Desktop 18 for Mac(Pd虚拟机) 18.3.1通用版

Parallels Desktop 18是一款虚拟机软件&#xff0c;能够让Mac电脑上运行Windows、Linux和其他操作系统的应用程序。 此版本的Parallels Desktop 18提供了多项功能增强和改进&#xff0c;包括更快的性能、更好的图形处理、更简单的导入和导出虚拟机等。该软件还支持Apple M1芯片…

QT使用按钮打开新窗口

需求说明&#xff1a;主窗口名为mainwindow&#xff0c;在主窗口添加一个按钮&#xff0c;通过点击按钮能打开一个新的窗口。 第一步&#xff1a;在主窗口添加按钮 找到左边菜单栏的按钮控件拖出置窗口上 第二步&#xff1a;在工程里新建窗口 1.右击最顶层项目文件名&#x…

Springcloud之Feign、Hystrix、Ribbon如何设置超时时间

一&#xff0c;概述 我们在微服务调用服务的时候&#xff0c;会使用hystrix、feign和ribbon&#xff0c;比如有一个实例发生了故障而该情况还没有被服务治理机制及时的发现和摘除&#xff0c;这时候客户端访问该节点的时候自然会失败。 所以&#xff0c;为了构建更为健壮的应…