Java基础-Atomic原子类

Java基础-Atomic原子类

一、Atomic 原子类简介

Atomic原子:指一个操作是不可中断的,即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。所谓原子类说简单点就是具有原子/原子操作特征的类。并发包java.util.concurrent的原子类都存放在java.util.concurrent.atomic下,如下图所示:

在这里插入图片描述

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

二、基本类型

使用原子的方式更新基本类型。
 ■ AtomicInteger:整型原子类
 ■ AtomicLong:长整型原子类
 ■ AtomicBoolean :布尔型原子类

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

public final int get() // 获取当前值
public final int getAndSet(int newValue) // 获取当前值,并设置新的值
public final boolean compareAndSet(int expect, int update) // 如果输入的数值等于预期值,则以原子方式将该值设置为输入值
public final int getAndIncrement() // 获取当前的值,并自增
public final int getAndDecrement() // 获取当前的值,并自减
public final int getAndAdd(int delta) // 获取当前的值,并加上预期的值

AtomicInteger常见方法使用

public class AtomicIntegerTest {public static void main(String[] args) {int temvalue = 0;AtomicInteger i = new AtomicInteger(0);temvalue = i.getAndSet(3);System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:0;i:3temvalue = i.getAndIncrement();System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:3;4temvalue = i.getAndAdd(5);System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:4;9}
}

AtomicInteger线程安全原理简单分析: AtomicInteger类的部分源码:

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }
}private volatile int value;

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

volatile修饰的变量被修改时,会将修改后的变量直接写入主存中,并且将其他线程中该变量的缓存置为无效,从而让其它线程对该变量的引用直接从主存中获取数据,这样就保证了变量的可见性。
但是volatile修饰的变量在自增时由于该操作分为读写两个步骤,所以当一个线程的读操作被阻塞时,另一个线程同时也进行了自增操作,此时由于第一个线程的写操作没有进行所以主存中仍旧是之前的原数据,所以当两个线程自增完成后,该变量可能只加了1。因而volatile是无法保证对变量的任何操作都是原子性的。

CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe类的objectFieldOffset()方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址。另外value是一个volatile变量,在内存中可见,因此JVM可以保证任何时刻任何线程总能拿到该变量的最新值。

三、数组类型

使用原子的方式更新数组里的某个元素
 ■ 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 boolean compareAndSet(int i, int expect, int update) // 如果输入的数值等于预期值,则以原子方式将 index = i 位置元素值设置为输入值
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 位置元素的值,并加上预期的值
public final void lazySet(int i, int newValue) // 最终将 index = i位置的元素设置为 newVlaue,使用 lazySet 设置之后可能导致其它线程在之后的一段时间内还可以读到旧的值。

AtomicIntegerArray常见方法使用

public class AtomicIntegerArrayTest {public static void main(String[] args) {int temvalue = 0;int[] nums = {1,2,3,4,5,6};AtomicIntegerArray i = new AtomicIntegerArray(nums);for (int j = 0; j < nums.length; j++) {System.out.println(i.get(j));}temvalue = i.getAndSet(0,2);System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:1; i:[2, 2, 3, 4, 5, 6]temvalue = i.getAndIncrement(0);System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:2; i:[3, 2, 3, 4, 5, 6]temvalue = i.getAndAdd(0,5);System.out.println("temvalue:" + temvalue + "; i:" + i); //temvalue:3; i:[8, 2, 3, 4, 5, 6]}
}

四、引用类型

基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用引用类型原子类。
 ■ AtomicReference: 引用类型原子类
 ■ AtomicMarkableReference:原子更新带有标记的引用类型。该类将boolean标记与引用关联起来,不能解决ABA问题。
 ■ AtomicStampedReference :原子更新带有版本号的引用类型。该类将整数值与引用关联起来, 可用于解决原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

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

@Test
public void AtomicReferenceTest() {AtomicReference<Person> personAtomicReference = new AtomicReference<>();Person person = new Person("zzx", 20);personAtomicReference.set(person);Person updatePerson = new Person("fanl", 22);personAtomicReference.compareAndSet(person, updatePerson);System.out.println(personAtomicReference.get().getName()); // fanlSystem.out.println(personAtomicReference.get().getAge()); // 22
}

上述代码首先创建了一个Person对象,然后把Person对象设置进AtomicReference对象中,然后调用compareAndSet方法,该方法就是通过CAS操作设置personAtomicReference。如果personAtomicReference的值为person的话,则将其设置为updatePerson。实现原理与 AtomicInteger类中的compareAndSet方法相同。

AtomicStampedReference类使用示例

    @Testpublic void AtomicStampedReferenceDemo() {// 实例化、取当前值和 stamp值final Integer initialRef = 0, initialStamp = 0;final AtomicStampedReference<Integer> asr = new AtomicStampedReference<>(initialRef, initialStamp);System.out.println("currentValue=" + asr.getReference() + ",currentStamp=" + asr.getStamp()); // currentValue=0,currentStamp=0// compare and setfinal Integer newReference = 666, newStamp = 999;final boolean casResult = asr.compareAndSet(initialRef,newReference,initialStamp,newStamp);System.out.println("currentValue=" + asr.getReference() + ",currentStamp=" + asr.getStamp() + ",casResult=" + casResult); // currentValue=666,currentStamp=999,casResult=true// 获取当前的值和当前的 stamp 值int[] arr = new int[1];final Integer currentValue = asr.get(arr);final int currentStamp = arr[0];System.out.println("currentValue=" + currentValue + ",currentStamp=" + currentStamp); // currentValue=666,currentStamp=999// 单独设置 stamp值final boolean attemptStampResult = asr.attemptStamp(newReference, 88);System.out.println("currentValue=" + asr.getReference() + ",currentStamp=" + asr.getStamp() + ", attemptStampResult=" + attemptStampResult); // currentValue=666,currentStamp=88, attemptStampResult=true// 重新设置当前值和 stamp值asr.set(initialRef, initialStamp);System.out.println("currentValue=" + asr.getReference() + ",currentStamp=" + asr.getStamp()); // currentValue=0,currentStamp=0}

AtomicMarkableReference类使用示例

@Test
public void AtomicMarkableReferenceDemo() {//实例化,取当前值和mark值final Boolean initialRef = null, initialMark = false;final AtomicMarkableReference<Boolean> amr = new AtomicMarkableReference<>(initialRef, initialMark);System.out.println("currentValue=" + amr.getReference() + ",currentMark=" + amr.isMarked()); // currentValue=null,currentMark=false// compare and setfinal Boolean newReference = true, newMark = true;final boolean casResult = amr.compareAndSet(initialRef, newReference, initialMark, newMark);System.out.println("currentValue=" + amr.getReference() + ",currentMark=" + amr.isMarked() + ", casResult=" + casResult);// currentValue=true,currentMark=true, casResult=true// 获取当前的值和当前的 mark值boolean[] arr = new boolean[1];final Boolean currentValue = amr.get(arr);final boolean currentMark = arr[0];System.out.println("currentValue=" + currentValue + ",currentMark=" + currentMark);// currentValue=true,currentMark=true// 单独设置 mark值final boolean attemptMarkResult = amr.attemptMark(newReference, false);System.out.println("currentValue=" + amr.getReference() + ",currentMark=" + amr.isMarked() + ", attemptMarkResult=" + attemptMarkResult);// currentValue=true,currentMark=false, attemptMarkResult=true// 重新设置当前值和 mark值amr.set(initialRef, initialMark);System.out.println("currentValue=" + amr.getReference() + ",currentMark=" + amr.isMarked());// currentValue=null,currentMark=false
}

案例二

// AtomicMarkableReference是将一个boolean值看作是否有更改的标记,本质就是它的版本号只有两个,true和false,修改的时候在这两个版本号之间切换,这样做并不能解决ABA问题,只是会降低ABA问题的发生的几率而已。public class SolveABAByAtomicMarkableReference {private static AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(100, false);private static void main(String[] args) {Thread refT1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStaticTrace();}atomicMarkableReference.compareAndSet(100, 101, atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked());atomicMarkableReference.compareAndSet(101, 100, atomicMarkableReference.isMarked(), !atomicMarkableReference.isMarked())});Thread refT2 = new Thread(() -> {boolean marked = atomicMarkableReference.isMarked();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStaticTrace();}boolean c3 = atomicMarkableReference.compareAndSet(100, 101, marked, !marked);system.out.println(c3); // 返回 true,实际应该返回 false});refT1.start();refT2.start();}
}

五、对象的属性修改类型

如果需要原子更新某个类里的某个字段时,需要用到对象的属性修改类型原子类。
 ■ AtomicIntegerFieldUpdater:原子更新整型字段的更新器
 ■ AtomicLongFieldUpdater:原子更新长整型字段的更新器
 ■ AtomicReferenceFieldUpdater:原子更新引用类型里的字段

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

@Test
public void AtomicIntegerFieldUpdateTest() {AtomicIntegerFieldUpdater<Person> updater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");Person person = new Person("Java", 20);// 20System.out.println(updater.getAndIncrement(person)); // 21System.out.println(updater.get(person));
}class Person {private String name;public volatile int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}

枚举类: 就是对象的实例个数是确定的(例如:单例模式),也就说我们在创建枚举类的时候,会对构造器进行设置。

一、自定义创建枚举类

package com.zzx.java;public class TestSeason {public static void main(String[] args) {Season spring = Season.SPRING;System.out.println(spring);spring.show();System.out.println(spring.getSeasonName());}
}
//枚举类
class Season{//1.提供类的属性,声明为private finalprivate final String seasonName;private final String seasonDesc;//2.声明为final的属性,在构造器中初始化。private Season(String seasonName,String seasonDesc){this.seasonName = seasonName;this.seasonDesc = seasonDesc;}//3.通过公共的方法来调用属性public String getSeasonName() {return seasonName;}public String getSeasonDesc() {return seasonDesc;}//4.创建枚举类的对象:将类的对象声明public static finalpublic static final Season SPRING = new Season("spring", "春暖花开");public static final Season SUMMER = new Season("summer", "夏日炎炎");public static final Season AUTUMN = new Season("autumn", "秋高气爽");public static final Season WINTER = new Season("winter", "白雪皑皑");@Overridepublic String toString() {return "Season [seasonName=" + seasonName + ", seasonDesc="+ seasonDesc + "]";}public void show(){System.out.println("这是一个季节");}
}

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

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

相关文章

谷粒商城实战-58-商品服务-API-三级分类-删除-批量删除小结

文章目录 一&#xff0c;增加一个批量删除的按钮并绑定事件二&#xff0c;全栈工程师三&#xff0c;逆向工程在全栈开发中的应用提升效率的方式&#xff1a;使用案例&#xff1a; 这一节的主要内容是开发批量删除分类的功能。 一&#xff0c;增加一个批量删除的按钮并绑定事件 …

树莓派智能家居中枢

一个先进的枢纽&#xff0c;使智能家居系统更智能、更可定制、更易于控制 Homey Pro由树莓派 Compute Module 4 供电,Homey Pro 为用户提供了一个单一界面,用于控制和监控来自不同品牌的所有智能家居设备。它完全在本地网络上运行,而不是依赖云端,从而实现了最低的延迟、最高的…

【数据结构】单链表的增删改查

介绍 链表是有序的列表&#xff0c;但是它在内存中是如下存储的&#xff1a; ①链表以节点的方式进行存储&#xff0c;是链式存储的 ②每个节点包含 data 域、next 域&#xff1a;指向下一节点 ③链表的各个节点不一定是连续存放的 ④链表分为有头节点的链表和没有头节点的链表…

netty入门-6 Handler和Pipeline

前言 书上讲服务器客户端创建三个要点&#xff0c;线程模型(Group)&#xff0c;IO模型(NioSocketChannel)&#xff0c;处理逻辑。 这篇的Handler和Pipeline&#xff0c;就是我们IO操作的处理逻辑。 然后下篇说ByteBuf这个Netty自己实现的数据封装组件。 Handler和Pipeline 我…

GAT知识总结

《GRAPH ATTENTION NETWORKS》 解决GNN聚合邻居节点的时候没有考虑到不同的邻居节点重要性不同的问题&#xff0c;GAT借鉴了Transformer的idea&#xff0c;引入masked self-attention机制&#xff0c; 在计算图中的每个节点的表示的时候&#xff0c;会根据邻居节点特征的不同来…

57 数据链路层

用于两个设备&#xff08;同一种数据链路节点&#xff09;之间传递 目录 对比理解“数据链路层” 和 “网络层”以太网 2.1 认识以太网 2.2 以太网帧格式MAC地址 3.1 认识MAC地址 3.2 对比理解MAC地址和IP地址局域网通信MTU 5.1 认识MTU 5.2 MTU对ip协议的影响 5.3 MTU对UDP的…

javafx的ListView代入项目的使用

目录 1. 创建一个可观察的列表&#xff0c;用于存储ListView中的数据,这里的User是包装了用户的相关信息。 2.通过本人id获取friendid&#xff0c;及好友的id&#xff0c;然后用集合接送&#xff0c;更方便直观一点。 3.用for遍历集合&#xff0c;逐个添加。 4.渲染器&…

非凸T0算法,如何获取超额收益?

什么是非凸 T0 算法&#xff1f; 非凸 T0 算法基于投资者持有的股票持仓&#xff0c;利用机器学习等技术&#xff0c;短周期预测&#xff0c;全自动操作&#xff0c;抓取行情波动价差&#xff0c;增厚产品收益。通过开仓金额限制、持仓时长控制等&#xff0c;把控盈亏风险&…

MySQL练习05

题目 步骤 触发器 use mydb16_trigger; #使用数据库create table goods( gid char(8) primary key, name varchar(10), price decimal(8,2), num int);create table orders( oid int primary key auto_increment, gid char(10) not null, name varchar(10), price decima…

基于Python的二手房价格分析与多种机器学习房价预测

需要本项目的同学可以私信我&#xff0c;提供部署讲解服务和文档 近年来&#xff0c;中国各个城市的房价问题一直是人们所关心的焦点之一。随着新建房价的不断上涨&#xff0c;城市内建筑新房的用地也越来越少&#xff0c;加上对房屋刚性的需求&#xff0c;人民群众对二手房的…

rust 初探 -- use

rust 初探 – use Package, Crate, 定义 Module use 关键字 作用&#xff1a;将路径引入到作用域内&#xff0c;其依旧遵循私有性规则&#xff0c;也即只用 pub 的部分引入进来才能使用 use crate::front_of_house::hosting; // 绝对路径 // use front_of_house::hosting; …

爬取贴吧的标题和链接

免责声明 感谢您学习本爬虫学习Demo。在使用本Demo之前&#xff0c;请仔细阅读以下免责声明&#xff1a; 学习和研究目的&#xff1a;本爬虫Demo仅供学习和研究使用。用户不得将其用于任何商业用途或其他未经授权的行为。合法性&#xff1a;用户在使用本Demo时&#xff0c;应确…

个性化音频生成GPT-SoVits部署使用和API调用

一、训练自己的音色模型步骤 1、准备好要训练的数据&#xff0c;放在Data文件夹中&#xff0c;按照文件模板中的结构进行存放数据 2、双击打开go-webui.bat文件&#xff0c;等待页面跳转 3、页面打开后&#xff0c;开始训练自己的模型 &#xff08;1&#xff09;、人声伴奏分…

关于sqlite数据库转化mysql数据

使用工具 下图所使用的为navivat premium 16数据库管理工具。 如下图所示为sqlite数据库db数据 下图为所设计的sqlite数据表格字段属性 首先导出sql语句 打开工具栏中的数据传输功能。 如上图所示&#xff0c;选择目标选为文件&#xff0c;并且将默认勾选的与源服务器相同…

oracle读写时相关字符集详解

服务器端操作系统&#xff08;Oracle linux&#xff09;字符集 服务器端数据库字符集 客户端操作系统&#xff08;Oracle linux&#xff09;字符集 客户端工具sqlplus字符集 结论1&#xff1a;客户端工具sqlplus的会话&#xff0c;使用的字符集&#xff0c;是数据库字符集。…

Android 15 适配整理——实践版

背景 谷歌发布Android 15后&#xff0c;国内的手机厂商迅速行动&#xff0c;开始了新系统的适配工作。小米、OPPO、vivo和联想等金标联盟成员联合发布了适配公告&#xff0c;督促APP开发者在2024年8月31日前完成适配工作&#xff0c;否则将面临搜索标签提示、应用降级、分机型…

zookeeper开启SASL权限认证

目录 一、SASL介绍 二、使用 SASL 进行身份验证 2.1 服务器到服务器的身份验证 2.2 客户端到服务器身份验证 三、验证功能 一、SASL介绍 默认情况下&#xff0c;ZooKeeper 不使用任何形式的身份验证并允许匿名连接。但是&#xff0c;它支持 Java 身份验证与授权服务(JAAS)…

学习日记:数据类型2

目录 1.转义字符 2.隐式类型转换 2.1 强制类型转换 2.2 不同类型间赋值 3.运算符 表达式 3.1 算术运算符 3.2 算术运算优先级 3.3 赋值运算 3.3.1 不同类型间混合赋值 3.4 逗号运算 4.生成随机数 5. 每日一练 1.转义字符 \n 表示换行 \t …

3.2、数据结构-数组、矩阵和广义表

数组结构 数组是定长线性表在维度上的扩展,即线性表中的元素又是一个线性表。N维数组是一种“同构”的数据结构,其每个数据元素类型相同、结构一致。 一个m行n列的数组表示如下: 其可以表示为行向量形式&#xff08;一行一行的数据&#xff09;或者列向量形式&#xff08;一…

【Python第三方库】PyQt5安装与应用

文章目录 引言安装PYQT5基于Pyqt5的简单桌面应用常用的方法与属性QtDesigner工具使用与集成窗口类型QWidget和QMainWindow区别 UI文件加载方式直接加载UI文件的方式显示窗口转化py文件进行显示窗口 PyQt5中常用的操作信号与槽的设置绑定页面跳转 引言 PyQt5是一个流行的Python…