37.ABA问题

变量初始值为A,修改成B,通过AtomicReference的compareAndSet去改变。就会先比较原来的值是否为A,如果为A则修改成功,否则修改失败。

但是如果在这个过程中,A变成D然后又变成A, 那么再调用compareAndSet去改变也能修改成功B。这个过程中A->D->A,程序是感知不到的。

@Slf4j
public class ABADemo {static AtomicReference<String> atomicReference = new AtomicReference<>("A");public static void main(String[] args) {String prev = atomicReference.get();//中间A->B->Aother();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}atomicReference.compareAndSet(prev, "C");log.info("atomicReference===={}", atomicReference.get());}public static void other() {new Thread(() -> {atomicReference.compareAndSet(atomicReference.get(), "B");log.info("atomicReference===={}", atomicReference.get());}, "t1").start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {atomicReference.compareAndSet(atomicReference.get(), "A");log.info("atomicReference===={}", atomicReference.get());}, "t2").start();}
}

上面的代码:主线程仅能判断共享变量的值是否与最初的A是否相同,不同感知这种从A变成了B,又变成了A的情景。

需求

如果主线程希望,只要有其他线程动过这个共享变量,那么自己cas操作就算失败,这时比较值是不够的,需要加入版本号。

谁做了修改,让这个版本号加一。

AtomicStampedReference  时间戳或者版本号

@Slf4j
public class ABADemo {//第一个此参数是值,第二个参数是版本号static AtomicStampedReference<String> atomicReference = new AtomicStampedReference<>("A", 1);public static void main(String[] args) {String prev = atomicReference.getReference();//版本号int stamp = atomicReference.getStamp();//中间A->B->Aother();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//旧的值,新的值,旧的版本号,新的版本号(+1)boolean res = atomicReference.compareAndSet(prev, "C", stamp, stamp + 1);log.info("更新结果={},atomicReference===={}", res, atomicReference.getReference());}public static void other() {new Thread(() -> {//获取当前数据的版本号int stamp = atomicReference.getStamp();boolean res = atomicReference.compareAndSet(atomicReference.getReference(), "B", stamp, stamp+1);log.info("更新结果={},atomicReference===={}", res, atomicReference.getReference());}, "t1").start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {//获取当前数据的版本号int stamp = atomicReference.getStamp();boolean res = atomicReference.compareAndSet(atomicReference.getReference(), "A", stamp, stamp+1);log.info("更新结果={},atomicReference===={}", res, atomicReference.getReference());}, "t2").start();}
}

通过AtomicStampedReference的版本号,可以知道数据改了多少次。但是有时候我们不关心数据改了多少次,只是想知道数据中间有没有改变过。所以就可以使用AtomicMarkableReference

@Slf4j
public class Test01 {public static void main(String[] args) {GarbageBag garbageBag = new GarbageBag("装满了垃圾");//第二个参数作为一个标记,true表示垃圾袋满了AtomicMarkableReference<GarbageBag> atomicMarkableReference = new AtomicMarkableReference<>(garbageBag, true);log.info("start....");GarbageBag prev = atomicMarkableReference.getReference();log.info("prev==={}", prev);//保洁阿姨去换了空垃圾袋new Thread(() -> {boolean res = atomicMarkableReference.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);log.info("res={}, garbageBag={}", res, atomicMarkableReference.getReference());}, "保洁阿姨").start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//换空垃圾袋失败,因为保洁阿姨已经换了,标记为false,垃圾袋空了log.info("换一个新的垃圾袋");boolean res = atomicMarkableReference.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);log.info("res={}, garbageBag={}", res, atomicMarkableReference.getReference());}
}class GarbageBag {private String desc;public GarbageBag(String desc) {this.desc = desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return super.toString()+"----"+desc;}
}

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

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

相关文章

Redis -- 缓存击穿问题

缓存击穿问题也叫热点Key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了&#xff0c;无数的请求访问会在瞬间给数据库带来巨大的冲击。 常见的解决方案有两种&#xff1a; 互斥锁 逻辑过期 逻辑分析&#xff1a;假设线程1在查询缓存之后&…

【Leetcode 347】,前k个高频元素,小根堆的调整

参考题解 题目&#xff1a;给定一个数组&#xff0c;输出 前k个高频元素。 思路&#xff1a; 遍历数组&#xff0c;建立小根堆&#xff08;小根堆的元素是元组&#xff08;num,freq&#xff09;&#xff0c;排序规则是每个元素的频率&#xff09;。 下面使用数组‘heap’&…

静态路由协议实验1

要求&#xff1a; 使用静态路由协议使得全网可达。 第一步、规划IP地址。并配置IP。 第二步、写静态路由 [r1]ip route-static 192.168.3.0 24 192.168.2.2 [r1]ip route-static 192.168.4.0 24 192.168.2.2 [r1]ip route-static 192.168.5.0 24 192.168.2.2[r2]ip route-st…

一、next-auth 身份验证凭据-使用电子邮件和密码注册登录

一、next-auth 身份验证凭据-使用电子邮件和密码注册登录 文章目录 一、next-auth 身份验证凭据-使用电子邮件和密码注册登录一、前言二、前置准备1、环境配置2、相关库安装&#xff08;1&#xff09;vercel 配置&#xff08;2&#xff09;Yarn 包管理配置 3、next项目初始化与…

【Oracle】oracle、mysql、sql server三者区别

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Oracle》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识…

并查集(基础+带权以及可撤销并查集后期更新)

并查集 并查集是一种图形数据结构&#xff0c;用于存储图中结点的连通关系。 每个结点有一个父亲&#xff0c;可以理解为“一只伸出去的手”&#xff0c;会指向另一个点&#xff0c;初始时指向自己。一个点的根节点是该点的父亲的父亲的..的父亲&#xff0c;直到某个点的父亲…

Shell脚本之基础-2

目录 一、字符处理 cut命令 awk命令 sed命令 字符串排序 二、条件判断 文件类型判断 文件权限判断 两个文件的判断 整数比较 字符串判断 多重判断 三、流程控制 if分支 if else 双分支结构 case分支 for循环 while循环 一、字符处理 cut命令 命令格式&#x…

Linux下I2C设备驱动:I2C设备和驱动匹配过程

一. 简介 Linux 内核也将 I2C 驱动分为两部分&#xff1a; (1) I2C 总线驱动&#xff0c; I2C 总线驱动就是 SOC 的 I2C 控制器驱动&#xff0c;也叫做 I2C 适配器驱动。 (2) I2C 设备驱动&#xff0c; I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。 本文来学习一下…

深入剖析JavaScript中的this(下)

五、事件处理函数的this 5.1 事件绑定 <button id"btn">点击我</button>function handleClick(e) {console.log(this);// <button id"btn">点击我</button> }document.getElementById(btn).addEventListener(click, handleClick…

CSS基础:4种简单选择器的详解

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。大专生&#xff0c;2年时间从1800到月入过万&#xff0c;工作5年买房。 分享成长心得。 261篇原创内容-公众号 后台回复“前端工具”可获取开发工具&#xff0c;持续更新中 后台回复“前端基础…

MySQL客户端安装并配置免密登录

最近在写脚本时需要向MySQL数据库中存储数据&#xff0c;且脚本运行的服务器与MySQL服务器不是同一台服务器&#xff0c;而且需要保证MySQL密码的安全性&#xff0c;不能在脚本中暴露&#xff0c;所以就需要在服务器上安装MySQL客户端&#xff0c;并配置免密登录。 一、虚拟机…

Git安装教程(图文安装)

Git Bash是git(版本管理器)中提供的一个命令行工具&#xff0c;外观类似于Windows系统内置的cmd命令行工具。 可以将Git Bash看作是一个终端模拟器&#xff0c;它提供了类似于Linux和Unix系统下Bash Shell环境的功能。通过Git Bash&#xff0c;用户可以在Windows系统中运行基于…

顺序表的动态实现

文章目录 静态顺序表和动态顺序表的异同动态顺序表的实现头文件定义结构体初始化打印顺序表 实现增删查改尾插头插尾删头删删掉某个位置的数据在某个位置插入数据查找某个位置的下标修改某个位置的数据扩容销毁顺序表 静态顺序表和动态顺序表的异同 相同: 内存空间连续&#xf…

Vue项目登录页实现获取短信验证码的功能

之前我们写过不需要调后端接口就获取验证码的方法,具体看《无需后端接口,用原生js轻松实现验证码》这个文章。现在我们管理后台有个需求,就是登录页面需要获取验证码,用户可以输入验证码后进行登录。效果如下,当我点击获取验证码后能获取短信验证码: 这里在用户点击获取…

C# 委托与事件 深入

委托委托最大的好处委托的第二个作用就是可以多播泛型委托预定义的委托*EventHandler 委托&#xff1a;* 事件事件就是一种特殊的委托举个例子微软为我们提供了标准事件模式 委托 这是一个简单的委托样例 class TODO {public static void Main(String[] args) {Cal aa new C…

Linux 线程:线程同步、生产者消费者模型

目录 一、死锁 二、条件变量实现线程同步 1、为什么需要线程同步 2、条件变量、同步、竞态条件 3、条件变量函数&#xff1a;初始化 销毁 等待 唤醒 4、实现简单的多线程程序 不唤醒则一直等待 实现线程同步 三、生产者消费者 1、借助超市模型理解 2、优点 四、基于…

Java基础知识总结(39)

1、今天学了什么 &#xff08;1&#xff09;构造器 构造器的定义&#xff1a; 需要注意的是构造器是一种特殊的方法&#xff0c;其方法名和类名相同&#xff0c;但没有方法返回值&#xff0c;也不用void修饰。 [修饰符] 方法名(形参列表){方法体 } 修饰符&#xff1a;修饰符可…

数字乡村创新实践探索:科技赋能农业现代化与乡村治理体系现代化同步推进

随着信息技术的飞速发展&#xff0c;数字乡村作为乡村振兴的重要战略方向&#xff0c;正日益成为推动农业现代化和乡村治理体系现代化的关键力量。科技赋能下的数字乡村&#xff0c;不仅提高了农业生产的效率和品质&#xff0c;也为乡村治理带来了新的机遇和挑战。本文旨在探讨…

Linux 环境下 Redis基础配置及开机自启

Linux 环境下 Redis基础配置及开机自启 linux环境安装redis<redis-6.0.5.tar.gz> 1-redis基本安装配置 解压 获取到tar包后&#xff0c;解压到相关目录&#xff0c;一般是将redis目录放在usr/local/redis目录下&#xff0c;可以使用-C指定到解压下目录 tar -zvxf re…

Java数据结构栈

栈&#xff08;Stack&#xff09; 概念 栈是一种先进后出的数据结构。 栈的使用 import java.util.Stack; public class Test {public static void main(String[] args) {Stack<Integer> s new Stack();s.push(1);s.push(2);s.push(3);s.push(4);System.out.println(s…