JUC 包中的 Atomic 原子类总结

news2024/9/24 5:30:56

人不走空

                                                                      

      🌈个人主页:人不走空      

💖系列专栏:算法专题

⏰诗词歌赋:斯是陋室,惟吾德馨

目录

      🌈个人主页:人不走空      

💖系列专栏:算法专题

⏰诗词歌赋:斯是陋室,惟吾德馨

Atomic 原子类介绍

基本类型原子类

数组类型原子类

引用类型原子类

对象的属性修改类型原子类

作者其他作品:


 

图片

JavaGuide官方网站javaguide.cn

Atomic 原子类介绍

Atomic 翻译成中文是“原子”的意思。在化学上,原子是构成物质的最小单位,在化学反应中不可分割。在编程中,Atomic 指的是一个操作具有原子性,即该操作不可分割、不可中断。即使在多个线程同时执行时,该操作要么全部执行完成,要么不执行,不会被其他线程看到部分完成的状态。

原子类简单来说就是具有原子性操作特征的类。

java.util.concurrent.atomic 包中的 Atomic 原子类提供了一种线程安全的方式来操作单个变量。

Atomic 类依赖于 CAS(Compare-And-Swap,比较并交换)乐观锁来保证其方法的原子性,而不需要使用传统的锁机制(如 synchronized 块或 ReentrantLock)。

这篇文章我们只介绍 Atomic 原子类的概念,具体实现原理可以阅读笔者写的这篇文章:什么是乐观锁和悲观锁?Java 中 CAS 是如何实现的?。

图片

JUC原子类概览

根据操作的数据类型,可以将 JUC 包中的原子类分为 4 类:

1、基本类型

使用原子的方式更新基本类型

  • AtomicInteger:整型原子类

  • AtomicLong:长整型原子类

  • AtomicBoolean:布尔型原子类

2、数组类型

使用原子的方式更新数组里的某个元素

  • AtomicIntegerArray:整型数组原子类

  • AtomicLongArray:长整型数组原子类

  • AtomicReferenceArray:引用类型数组原子类

3、引用类型

  • AtomicReference:引用类型原子类

  • AtomicMarkableReference:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来,也可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

🐛 修正(参见:issue#626:https://github.com/Snailclimb/JavaGuide/issues/626 : AtomicMarkableReference 不能解决 ABA 问题。

4、对象的属性修改类型

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器

  • AtomicLongFieldUpdater:原子更新长整型字段的更新器

  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段

基本类型原子类

使用原子的方式更新基本类型

  • AtomicInteger:整型原子类

  • AtomicLong:长整型原子类

  • AtomicBoolean:布尔型原子类

上面三个类提供的方法几乎相同,所以我们这里以 AtomicInteger 为例子来介绍。

AtomicInteger 类常用方法 :

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 提供了一种比 set 方法更弱的语义,可能导致其他线程在之后的一小段时间内还是可以读到旧的值,但可能更高效。

AtomicInteger 类使用示例 :

// 初始化 AtomicInteger 对象,初始值为 0
AtomicInteger atomicInt = new AtomicInteger(0);

// 使用 getAndSet 方法获取当前值,并设置新值为 3
int tempValue = atomicInt.getAndSet(3);
System.out.println("tempValue: " + tempValue + "; atomicInt: " + atomicInt);

// 使用 getAndIncrement 方法获取当前值,并自增 1
tempValue = atomicInt.getAndIncrement();
System.out.println("tempValue: " + tempValue + "; atomicInt: " + atomicInt);

// 使用 getAndAdd 方法获取当前值,并增加指定值 5
tempValue = atomicInt.getAndAdd(5);
System.out.println("tempValue: " + tempValue + "; atomicInt: " + atomicInt);

// 使用 compareAndSet 方法进行原子性条件更新,期望值为 9,更新值为 10
boolean updateSuccess = atomicInt.compareAndSet(9, 10);
System.out.println("Update Success: " + updateSuccess + "; atomicInt: " + atomicInt);

// 获取当前值
int currentValue = atomicInt.get();
System.out.println("Current value: " + currentValue);

// 使用 lazySet 方法设置新值为 15
atomicInt.lazySet(15);
System.out.println("After lazySet, atomicInt: " + atomicInt);

输出:

tempValue: 0; atomicInt: 3
tempValue: 3; atomicInt: 4
tempValue: 4; atomicInt: 9
Update Success: true; atomicInt: 10
Current value: 10
After lazySet, atomicInt: 15

数组类型原子类

使用原子的方式更新数组里的某个元素

  • AtomicIntegerArray:整形数组原子类

  • AtomicLongArray:长整形数组原子类

  • AtomicReferenceArray:引用类型数组原子类

上面三个类提供的方法几乎相同,所以我们这里以 AtomicIntegerArray 为例子来介绍。

AtomicIntegerArray 类常用方法

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 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

AtomicIntegerArray 类使用示例 :

int[] nums = {1, 2, 3, 4, 5, 6};
// 创建 AtomicIntegerArray
AtomicIntegerArray atomicArray = new AtomicIntegerArray(nums);

// 打印 AtomicIntegerArray 中的初始值
System.out.println("Initial values in AtomicIntegerArray:");
for (int j = 0; j < nums.length; j++) {
    System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}

// 使用 getAndSet 方法将索引 0 处的值设置为 2,并返回旧值
int tempValue = atomicArray.getAndSet(0, 2);
System.out.println("\nAfter getAndSet(0, 2):");
System.out.println("Returned value: " + tempValue);
for (int j = 0; j < atomicArray.length(); j++) {
    System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}

// 使用 getAndIncrement 方法将索引 0 处的值加 1,并返回旧值
tempValue = atomicArray.getAndIncrement(0);
System.out.println("\nAfter getAndIncrement(0):");
System.out.println("Returned value: " + tempValue);
for (int j = 0; j < atomicArray.length(); j++) {
    System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}

// 使用 getAndAdd 方法将索引 0 处的值增加 5,并返回旧值
tempValue = atomicArray.getAndAdd(0, 5);
System.out.println("\nAfter getAndAdd(0, 5):");
System.out.println("Returned value: " + tempValue);
for (int j = 0; j < atomicArray.length(); j++) {
    System.out.print("Index " + j + ": " + atomicArray.get(j) + " ");
}

输出:

Initial values in AtomicIntegerArray:
Index 0: 1 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6 
After getAndSet(0, 2):
Returned value: 1
Index 0: 2 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6 
After getAndIncrement(0):
Returned value: 2
Index 0: 3 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6 
After getAndAdd(0, 5):
Returned value: 3
Index 0: 8 Index 1: 2 Index 2: 3 Index 3: 4 Index 4: 5 Index 5: 6 

引用类型原子类

基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。

  • AtomicReference:引用类型原子类

  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

  • AtomicMarkableReference:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来,也可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

上面三个类提供的方法几乎相同,所以我们这里以 AtomicReference 为例子来介绍。

AtomicReference 类使用示例 :

// Person 类
class Person {
    private String name;
    private int age;
    //省略getter/setter和toString
}


// 创建 AtomicReference 对象并设置初始值
AtomicReference<Person> ar = new AtomicReference<>(new Person("SnailClimb", 22));

// 打印初始值
System.out.println("Initial Person: " + ar.get().toString());

// 更新值
Person updatePerson = new Person("Daisy", 20);
ar.compareAndSet(ar.get(), updatePerson);

// 打印更新后的值
System.out.println("Updated Person: " + ar.get().toString());

// 尝试再次更新
Person anotherUpdatePerson = new Person("John", 30);
boolean isUpdated = ar.compareAndSet(updatePerson, anotherUpdatePerson);

// 打印是否更新成功及最终值
System.out.println("Second Update Success: " + isUpdated);
System.out.println("Final Person: " + ar.get().toString());

输出:

Initial Person: Person{name='SnailClimb', age=22}
Updated Person: Person{name='Daisy', age=20}
Second Update Success: true
Final Person: Person{name='John', age=30}

AtomicStampedReference 类使用示例 :

// 创建一个 AtomicStampedReference 对象,初始值为 "SnailClimb",初始版本号为 1
AtomicStampedReference<String> asr = new AtomicStampedReference<>("SnailClimb", 1);

// 打印初始值和版本号
int[] initialStamp = new int[1];
String initialRef = asr.get(initialStamp);
System.out.println("Initial Reference: " + initialRef + ", Initial Stamp: " + initialStamp[0]);

// 更新值和版本号
int oldStamp = initialStamp[0];
String oldRef = initialRef;
String newRef = "Daisy";
int newStamp = oldStamp + 1;

boolean isUpdated = asr.compareAndSet(oldRef, newRef, oldStamp, newStamp);
System.out.println("Update Success: " + isUpdated);

// 打印更新后的值和版本号
int[] updatedStamp = new int[1];
String updatedRef = asr.get(updatedStamp);
System.out.println("Updated Reference: " + updatedRef + ", Updated Stamp: " + updatedStamp[0]);

// 尝试用错误的版本号更新
boolean isUpdatedWithWrongStamp = asr.compareAndSet(newRef, "John", oldStamp, newStamp + 1);
System.out.println("Update with Wrong Stamp Success: " + isUpdatedWithWrongStamp);

// 打印最终的值和版本号
int[] finalStamp = new int[1];
String finalRef = asr.get(finalStamp);
System.out.println("Final Reference: " + finalRef + ", Final Stamp: " + finalStamp[0]);

输出结果如下:

Initial Reference: SnailClimb, Initial Stamp: 1
Update Success: true
Updated Reference: Daisy, Updated Stamp: 2
Update with Wrong Stamp Success: false
Final Reference: Daisy, Final Stamp: 2

AtomicMarkableReference 类使用示例 :

// 创建一个 AtomicMarkableReference 对象,初始值为 "SnailClimb",初始标记为 false
AtomicMarkableReference<String> amr = new AtomicMarkableReference<>("SnailClimb", false);

// 打印初始值和标记
boolean[] initialMark = new boolean[1];
String initialRef = amr.get(initialMark);
System.out.println("Initial Reference: " + initialRef + ", Initial Mark: " + initialMark[0]);

// 更新值和标记
String oldRef = initialRef;
String newRef = "Daisy";
boolean oldMark = initialMark[0];
boolean newMark = true;

boolean isUpdated = amr.compareAndSet(oldRef, newRef, oldMark, newMark);
System.out.println("Update Success: " + isUpdated);

// 打印更新后的值和标记
boolean[] updatedMark = new boolean[1];
String updatedRef = amr.get(updatedMark);
System.out.println("Updated Reference: " + updatedRef + ", Updated Mark: " + updatedMark[0]);

// 尝试用错误的标记更新
boolean isUpdatedWithWrongMark = amr.compareAndSet(newRef, "John", oldMark, !newMark);
System.out.println("Update with Wrong Mark Success: " + isUpdatedWithWrongMark);

// 打印最终的值和标记
boolean[] finalMark = new boolean[1];
String finalRef = amr.get(finalMark);
System.out.println("Final Reference: " + finalRef + ", Final Mark: " + finalMark[0]);

输出结果如下:

Initial Reference: SnailClimb, Initial Mark: false
Update Success: true
Updated Reference: Daisy, Updated Mark: true
Update with Wrong Mark Success: false
Final Reference: Daisy, Final Mark: true

对象的属性修改类型原子类

如果需要原子更新某个类里的某个字段时,需要用到对象的属性修改类型原子类。

  • AtomicIntegerFieldUpdater:原子更新整形字段的更新器

  • AtomicLongFieldUpdater:原子更新长整形字段的更新器

  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段的更新器

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

上面三个类提供的方法几乎相同,所以我们这里以 AtomicIntegerFieldUpdater为例子来介绍。

AtomicIntegerFieldUpdater 类使用示例 :

// Person 类
class Person {
    private String name;
    // 要使用 AtomicIntegerFieldUpdater,字段必须是 public volatile
    private volatile int age;
    //省略getter/setter和toString
}

// 创建 AtomicIntegerFieldUpdater 对象
AtomicIntegerFieldUpdater<Person> ageUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");

// 创建 Person 对象
Person person = new Person("SnailClimb", 22);

// 打印初始值
System.out.println("Initial Person: " + person);

// 更新 age 字段
ageUpdater.incrementAndGet(person); // 自增
System.out.println("After Increment: " + person);

ageUpdater.addAndGet(person, 5); // 增加 5
System.out.println("After Adding 5: " + person);

ageUpdater.compareAndSet(person, 28, 30); // 如果当前值是 28,则设置为 30
System.out.println("After Compare and Set (28 to 30): " + person);

// 尝试使用错误的比较值进行更新
boolean isUpdated = ageUpdater.compareAndSet(person, 28, 35); // 这次应该失败
System.out.println("Compare and Set (28 to 35) Success: " + isUpdated);
System.out.println("Final Person: " + person);

输出结果:

Initial Person: Name: SnailClimb, Age: 22
After Increment: Name: SnailClimb, Age: 23
After Adding 5: Name: SnailClimb, Age: 28
After Compare and Set (28 to 30): Name: SnailClimb, Age: 30
Compare and Set (28 to 35) Success: false
Final Person: Name: SnailClimb, Age: 30

本文来自于恩师 公众号Guide JavaGuide


作者其他作品:

【Java】Spring循环依赖:原因与解决方法

OpenAI Sora来了,视频生成领域的GPT-4时代来了

[Java·算法·简单] LeetCode 14. 最长公共前缀 详细解读

【Java】深入理解Java中的static关键字

[Java·算法·简单] LeetCode 28. 找出字a符串中第一个匹配项的下标 详细解读

了解 Java 中的 AtomicInteger 类

算法题 — 整数转二进制,查找其中1的数量

深入理解MySQL事务特性:保证数据完整性与一致性

Java企业应用软件系统架构演变史 

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

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

相关文章

RFID涉密载体管控系统|DW-S402功能介绍

文件载体管控系统DW-S402是用于对各种载体进行有效管理的智能柜&#xff08;智能管理系统&#xff09;&#xff0c;实现对载体的智能化、规范化、标准化管理&#xff0c;广泛应用于保密、机要单位以及企事业单位等有载体保管需求的行业。 区域监控管理 主要是通过在需要监控的…

el-table表格操作列错行处理

解决方法&#xff1a; <style>::v-deep .el-table th.el-table__cell > .cell {white-space: nowrap !important;} </style>

ArkUI状态管理

State装饰器 在声明式UI中&#xff0c;是以状态驱动试图更新 状态 (State) 指驱动视图更新的数据(被装饰器标记的变量) 试图(View) 基于UI描述渲染得到用户界面 说明 1.State装饰器标记的变量必须初始化&#xff0c;不能为空 2.State支持Object、classstring、number、b…

Self-Attention 自注意力机制(二)——实例过程说明

一、自注意力机制核心过程 自注意力机制&#xff08;Self-Attention Mechanism&#xff09;&#xff0c;也称为内部注意力机制&#xff0c;是一种在序列模型中用于捕捉序列内部不同位置之间依赖关系的技术。这种机制允许模型在处理序列时&#xff0c;对序列中的每个元素分配不…

基于 Web 的家校联系系统的设计与实现

目录 基于 Web 的家校联系系统的设计与实现 一、绪论 &#xff08;一&#xff09;研究背景 &#xff08;二&#xff09; 研究目的 &#xff08;三&#xff09; 研究意义 二、需求分析 &#xff08;一&#xff09; 功能需求 &#xff08;二&#xff09; 性能需求 &#…

【C++】类和对象的基本概念与使用

本文通过面向对象的概念以及通俗易懂的例子介绍面向对象引出类和对象。最后通过与之有相似之处的C语言中的struct一步步引出C中的类的定义方式&#xff0c;并提出了一些注意事项&#xff0c;最后描述了类的大小的计算方法。 一、什么是面向对象&#xff1f; 1.面向对象的概念 …

解决一下git clone失败的问题

1&#xff09;.不开梯子&#xff0c;我们用https克隆 git clone https://github.com 报错&#xff1a; Failed to connect to github.com port 443 after 2091 ms: Couldnt connect to server 解决办法&#xff1a; 开梯子&#xff0c;然后# 注意修改成自己的IP和端口号 gi…

自适应巡航控制中的Stop Go功能详解

自适应巡航控制中的跟车行驶功能详解 文章目录 1. 背景介绍2. 功能定义3. 功能原理4. 传感器架构5. 实际应用案例6. 总结与展望 1. 背景介绍 自适应巡航控制&#xff08;Adaptive Cruise Control, ACC&#xff09;系统中的Stop & Go功能是提升驾驶舒适性和安全性的重要子…

Qt Splash样式的登录窗

Qt Splash样式的登录窗实现 目录 0 结果 1. 设计界面 2.鼠标拖动界面 3. 密码MD5加密 4. 保用户名和密码到注册表 0 结果 1. 设计界面 继承自QDialog&#xff0c;图片使用QLabel加载&#xff0c;windowFlag样式为 Qt::SplashScreen TLoginForm::TLoginForm(QWidget *pa…

PPOCR训练后acc为0解决方法

首先你需要先配置PPOCR源码和环境&#xff0c;可以按照如下流程 PPOCR训练和C#部署英文字符训练_ppocrlabel可以终端运行吗-CSDN博客 训练时如下 如下是rec训练模型 acc为0 det也是如此 如下没有 名为best的模型文件 解决办法就是 将如下的train和eval部分的batch_siz…

挂耳式蓝牙耳机什么牌子好?这五款综合表现遥遥领先

为什么这几年开放式耳机受到了越来越多消费者的喜爱&#xff1f;我想是因为它全方位的弥补了入耳式耳机堵塞耳朵、不够安全健康的缺陷&#xff0c;真正做到了安全性与舒适性兼得。那么刚入坑开放式耳机的小白该如何挑选一款品质较高的开放式耳机呢&#xff1f;挂耳式蓝牙耳机什…

第59期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

Python酷库之旅-第三方库Pandas(027)

目录 ​一、用法精讲 68、pandas.infer_freq函数 68-1、语法 68-2、参数 68-3、功能 68-4、返回值 68-5、说明 68-6、用法 68-6-1、数据准备 68-6-2、代码示例 68-6-3、结果输出 69、pandas.interval_range函数 69-1、语法 69-2、参数 69-3、功能 69-4、返回值…

JVM--HostSpot算法细节实现

1.根节点枚举 定义&#xff1a; 我们以可达性分析算法中从GC Roots 集合找引用链这个操作作为介绍虚拟机高效实现的第一个例 子。固定可作为GC Roots 的节点主要在全局性的引用&#xff08;例如常量或类静态属性&#xff09;与执行上下文&#xff08;例如 栈帧中的本地变量表&a…

硬件开发笔记(二十六):AD21导入电感原理图库、封装库和3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140437290 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

NDK R25b 交叉编译FFMpeg4,项目集成,附库下载地址

1.准备工作 文件下载&#xff1a; NDK R25b下载地址&#xff1a;Android NDK历史版本下载网址 - 君*邪 - 博客园 (cnblogs.com) FFmpeg4.4.4 下载地址&#xff1a;https://ffmpeg.org/releases/ffmpeg-4.4.4.tar.xz 环境配置&#xff1a; 本次编译环境是在PC虚拟机中使用U…

水表数字识别2:Pytorch DBNet实现水表数字检测(含训练代码和数据集)

水表数字识别2&#xff1a;Pytorch DBNet实现水表数字检测(含训练代码和数据集) 目录 水表数字识别2&#xff1a;Pytorch DBNet实现水表数字检测(含训练代码和数据集) 1.前言 2. 水表数字识别的方法 3. 水表数字识别数据集 4. 水表数字分割模型训练 &#xff08;1&#x…

Oracle线上执行SQL特别慢的原因分析

一、背景&#xff1a; 线上反馈一张表select * from table where idxxx语句执行特别慢&#xff0c;超过60s超时不能处理&#xff0c;第一直觉是索引失效了&#xff0c;开始执行创建索引语句create index index_name on table() online。但是执行了超过20分钟索引还没有创建成功…

NestJs框架getRepository方法废弃解决办法,顺便介绍一下typeORM框架

报错&#xff1a; Connection "default" was not found. ... ...因为 typeorm 版本不对&#xff0c;这是旧版的 api, 可以考虑降版本到 <0.3.0 解决 介绍一下 typeORM 我们如果直接使用 Node.js 操作 mysql 提供的接口&#xff0c; 那么编写的代码就比较底层&a…

彩电上自带的推箱子游戏是什么编程语言开发的?

2000年左右的厦新彩电上&#xff0c;自带了推箱子、华容道游戏。界面如下&#xff1a; 在线版推箱子游戏&#xff0c;网址&#xff1a;https://www.tuixiangzi.cn/ BASIC&#xff0c;全称是Beginners All-purpose Symbolic Instruction Code&#xff0c;含义是初学者通用符号…