突击并发编程JUC系列-原子引用AtomicReference
突击并发编程JUC系列演示代码地址: https://github.com/mtcarpenter/JavaTutorial
小伙伴们,大家好,我们又见面了,突击并发编程 JUC 系列实战原子引用马上就要发车了。
原子引用
AtomicReference
类提供了对象引用的非阻塞原子性读写操作,对比原子更新基本类型的 AtomicInteger
,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic 包提供了以下 3 个类。
原子类型常用方法如下
方法名 |
说明 |
---|---|
|
以原子方式获取当前值 |
|
以原子方式赋值给定值 |
|
如果当前值 |
|
如果当前值为 |
|
将原子设置为给定值并返回旧值。 |
........ |
|
|
|
|
使用应用给定函数的结果原子更新当前值,返回上一个值。 |
|
使用给定函数的结果原子更新当前值,返回更新的值。 |
|
使用给定函数应用给当前值和给定值的结果原子更新当前值,返回上一个值。 |
|
使用将给定函数应用于当前值和给定值的结果原子更新当前值,返回更新后的值。 |
........ |
小试牛刀
为了帮助大家理解,通过教师投票数的场景,进行案例讲解,教师类被设计为不可变对象,一旦创建就无法进行修改。类中只包含两个字段名:教师名,得票数。具体教师类代码如下:
public class Teacher {
/**
* 教师名称
*/
public volatile String name;
/**
* 学生投票数
*/
public volatile int ticketNum;
public Teacher(String name, int ticketNum) {
this.name = name;
this.ticketNum = ticketNum;
}
public String getName() {
return name;
}
public int getTicketNum() {
return ticketNum;
}
}
AtomicReference 案例演示
这是一个天理不容的场景,小春哥一直兢兢业业工作,获得无数学生的芳心,临近期末通过学生投票评选一年一度的优秀教师,懵懂少年一直嫉妒我,拿到我的信息,解锁了最佳优秀的教师信箱,并自己的票数修改了,然后将信息放入信箱。代码演示如下:
public class AtomicExample6 {
/**
* 引用类型 AtomicReference
*/
private static AtomicReference<Teacher> atomicReference = new AtomicReference<>();
private static BinaryOperator<Teacher> binaryOperator = new BinaryOperator<Teacher>() {
@Override
public Teacher apply(Teacher teacher, Teacher teacher2) {
// 返回新值
return teacher2;
}
};
public static void main(String[] args) {
Teacher teacher = new Teacher("小春哥", 200);
// 将当前对象设置到引用对象 AtomicReference 中
atomicReference.set(teacher);
Teacher updateTeacher = new Teacher("懵懂少年", 180);
// teacher 和 引用类型AtomicReference 保存的对象一致 则能修改成功
atomicReference.compareAndSet(teacher, updateTeacher);
System.out.println(atomicReference.get().getNamne());
System.out.println(atomicReference.get().getTicketNum());
Teacher accumulateTeacher = new Teacher("懵懂少年", 210);
// 原子性地更新指定对象,并且返回AtomicReference更新后的值
atomicReference.accumulateAndGet(accumulateTeacher, binaryOperator);
System.out.println(atomicReference.get().getName());
System.out.println(atomicReference.get().getTicketNum());
}
}
被成功修改的结果如下:
懵懂少年
180
懵懂少年
210
AtomicReference
除了引用对象的使用,对于基本类型也是可以进行操作的。
AtomicReferenceFieldUpdater 案例演示
AtomicReferenceFieldUpdater
是一个基于反射的类,它允许原子更新指定类的指定voltile
字段。小春哥只是暂时代课,无法参与学生投票,学校就直接将我的票数,转交给了我帮忙代课的老师了。代码演示如下:
public class AtomicExample7 {
/**
* 引用类型 AtomicReferenceFieldUpdater
*/
private static AtomicReferenceFieldUpdater referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Teacher.class, String.class, "name");
public static void main(String[] args) {
Teacher teacher = new Teacher("小春哥", 200);
referenceFieldUpdater.compareAndSet(teacher, "小春哥", "公众号:山间木匠");
System.out.println("修改后的name为:" + teacher.getName());
System.out.println("修改后的票数为:" + teacher.getTicketNum());
}
}
被成功修改的结果如下:
修改后的name为:公众号:山间木匠
修改后的票数为:200
AtomicMarkableReference 案例演示
能解决 ABA
问题???,AtomicMarkableReference
通过 boolean
值作为是否更改的标记,所以他的版本号只有 true
和false
。在并发中如果两个版本号不断地切换,任然不能很好地解决 ABA
问题,只是从某种程度降低了ABA
事件发生。
当增强版信箱还没有关闭的时候,懵懂少年修改了之前的票数,修改之后害怕自己遭受质疑,通过上一次的版本又再一次修改了自己的票数。代码演示如下:
public class AtomicExample8 {
public static void main(String[] args) {
Teacher teacher = new Teacher( "小春哥",200);
AtomicMarkableReference<Teacher> markableReference = new AtomicMarkableReference<>(teacher, true);
Teacher newTeacher = new Teacher("懵懂少年",210);
System.out.println("当前返回状态: "+markableReference.compareAndSet(teacher, newTeacher, true, false));
System.out.println("AtomicMarkableReference 状态: "+markableReference.isMarked());
Teacher twoTeacher = new Teacher("懵懂少年",201);
System.out.println("当前返回状态 状态: "+markableReference.compareAndSet(newTeacher, twoTeacher, false, false));
System.out.println("AtomicMarkableReference 状态: " + markableReference.isMarked());
}
}
被成功修改的结果如下:
当前返回状态: true
AtomicMarkableReference 状态: false
当前返回状态 状态: true
AtomicMarkableReference 状态: false
总结
本章节通过案例演示了原子引用AtomicReference
、AtomicReferenceFieldUpdater
、AtomicMarkableReference
,感兴趣的小伙伴可以通过代码进行进一步的分析。
欢迎关注公众号 山间木匠 , 我是小春哥,从事 Java 后端开发,会一点前端、通过持续输出系列技术文章与文会友,如果本文能为您提供帮助,欢迎大家关注、 点赞、分享支持,我们下期再见!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 引入CSS样式表(书写位置)
- CSS复合选择器
- 不是吧,Serverless 还能这么玩?
- 这一行代码,能让你的 Python 运行速度提高 100 倍!
- 【52期】记一道简单的Java面试题,但答错率很高!
- 面向隐私 AI 的 TensorFlow 深度定制化实践
- 别再问如何用Python提取PDF内容了!
- gff文件转换成gtf文件
- R的save,load函数和 .rda文件
- 10行Python代码自动清理电脑内重复文件,解放双手!
- 【一天一大 lee】二叉树的后序遍历 (难度:中等) - Day20200929
- 一日一技:在Golang下如何相对简单地开发爬虫?
- 原创 | 一文读懂正态分布与贝塔分布
- 收款神器!解读聚合收款码背后的原理
- 短视频商城源码,滑动时渐变效果实现