aba会导致问题_浅谈Java中ABA问题及避免
本文主要研究的是关于Java中ABA问题及避免的相关内容,具体如下。
在《Java并发实战》一书的第15章中有一个用原子变量实现的并发栈,代码如下:
public class Node {
public final String item;
public Node next;
public Node(String item){
this.item = item;
}
}
public class ConcurrentStack {
AtomicReference top = new AtomicReference();
public void push(String item){
Node newTop = new Node(item);
Node oldTop;
do{
oldTop = top.get();
newTop.next = oldTop;
}
while(!top.compareAndSet(oldTop, newTop));
}
public String pop(){
Node newTop;
Node oldTop;
do{
oldTop = top.get();
if(oldTop == null){
return null;
}
newTop = oldTop.next;
}
while(!top.compareAndSet(oldTop, newTop));
return oldTop.item;
}
}
这个例子并不会引发ABA问题,至于为什么不会,后面再讲解,下面先讲一下ABA问题
什么是ABA?
引用原书的话:如果在算法中的节点可以被循环使用,那么在使用“比较并交换”指令就可能出现这种问题,在CAS操作中将判断“V的值是否仍然为A?”,并且如果是的话就继续执行更新操作,在某些算法中,如果V的值首先由A变为B,再由B变为A,那么CAS将会操作成功
ABA的例子
有时候,ABA造成的后果很严重,下面将并发栈的例子修改一下,看看ABA会造成什么问题:
public class Node {
public final String item;
public Node next;
public Node(String item){
this.item = item;
}
}
public class ConcurrentStack {
AtomicReference top = new AtomicReference();
public void push(Node node){
Node oldTop;
do{
oldTop = top.get();
node.next = oldTop;
}
while(!top.compareAndSet(oldTop, node));
}
public Node pop(int time){
Node newTop;
Node oldTop;
do{
oldTop = top.get();
if(oldTop == null){
return null;
}
newTop = oldTop.next;
TimeUnit.SECONDS.sleep(time);
}
while(!top.compareAndSet(oldTop, newTop));
return oldTop;
}
}
注意这里的变化,Node基本没有变化
重点关注ConcurrentStack的变化
1、push方法:原来是使用内容构造Node,现在直接传入Node,这样就符合了“在算法中的节点可以被循环使用”这个要求
2、pop方法的sleep,这是模拟线程的执行情况,以便观察结果
我们先往stack中压入两个Node:
ConcurrentStack stack = new ConcurrentStack();
stack.push(new Node("A"));
stack.push(new Node("B"));
然后创建两个线程来执行出入栈的操作
线程A先执行出栈:让NodeA出栈
stack.pop(3);
因为某些原因,线程A执行出栈比较久,用了3s
线程B执行出栈之后再入栈:先然NodeA和NodeB出栈,然后让NodeD,NodeC,NodeA入栈(NodeA在栈顶)
Node A = stack.pop(0);
stack.pop(0);
stack.push(new Node("D"));
stack.push(new Node("C"));
stack.push(A);
注意:线程B实现了节点的循环利用,它先将栈里面的内容全部出栈,然后入栈,最后栈顶的内容是之前出栈的Node
线程B执行完这些动作之后,线程A才执行CAS,此时CAS是可以执行成功的
按照原来的想法,线程A和B执行之后,stack的内容应该是:C和D,C在栈顶,但这里的执行结果却是Stack中什么都没有,这就是ABA问题
如何避免ABA问题
Java中提供了AtomicStampedReference和AtomicMarkableReference来解决ABA问题
AtomicStampedReference可以原子更新两个值:引用和版本号,通过版本号来区别节点的循环使用,下面看AtomicStampedReference的例子:
public class ConcurrentStack {
AtomicStampedReference top = new AtomicStampedReference(null,0);
public void push(Node node){
Node oldTop;
int v;
do{
v=top.getStamp();
oldTop = top.getReference();
node.next = oldTop;
}
while(!top.compareAndSet(oldTop, node,v,v+1));
// }while(!top.compareAndSet(oldTop, node,top.getStamp(),top.getStamp()+1));
}
public Node pop(int time){
Node newTop;
Node oldTop;
int v;
do{
v=top.getStamp();
oldTop = top.getReference();
if(oldTop == null){
return null;
}
newTop = oldTop.next;
try {
TimeUnit.SECONDS.sleep(time);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
while(!top.compareAndSet(oldTop, newTop,v,v+1));
// }while(!top.compareAndSet(oldTop, newTop,top.getStamp(),top.getStamp()));
return oldTop;
}
public void get(){
Node node = top.getReference();
while(node!=null){
System.out.println(node.getItem());
node = node.getNode();
}
}
}
注意:不能使用注释中的方式,否则就和单纯使用原子变量没有区别了
AtomicMarkableReference可以原子更新一个布尔类型的标记位和引用类型,看下面的例子:
AtomicMarkableReference top = new AtomicMarkableReference(null,true);
public void push(Node node){
Node oldTop;
Boolean v;
do{
v=top.isMarked();
oldTop = top.getReference();
node.next = oldTop;
}
while(!top.compareAndSet(oldTop, node,v,!v));
}
总结
以上就是本文关于浅谈Java中ABA问题及避免的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
aba会导致问题_浅谈Java中ABA问题及避免相关推荐
- java 线程aba,浅谈Java中ABA问题及避免,浅谈javaaba避免
浅谈Java中ABA问题及避免,浅谈javaaba避免 本文主要研究的是关于Java中ABA问题及避免的相关内容,具体如下. 在<Java并发实战>一书的第15章中有一个用原子变量实现的并 ...
- scale和java比较_浅谈java中BigDecimal的equals与compareTo的区别
这两天在处理支付金额校验的时候出现了点问题,有个金额比较我用了BigDecimal的equals方法来比较两个金额是否相等,结果导致金额比较出现错误(比如3.0与3.00的比较等). [注:以下所讲都 ...
- java中单例的应用_浅谈Java中单例模式的几种应用
目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...
- java 中的单元测试_浅谈Java 中的单元测试
单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...
- java中修饰常量的事_浅谈java中的声明常量为什么要用static修饰
今天定义一个类常量,想着也只有这个类可以用到,就没用static关键字修饰.结果sonar代码检查提示: Rename this field "PERSON_TYPE_USER" ...
- java中的强制类型转换注意事项_浅谈Java中强制类型转换的问题
为了更好的理解我们先看下面的例子: package com.yonyou.test; import java.util.ArrayList; import java.util.Iterator; im ...
- java布尔类型比较器_浅谈Java中几种常见的比较器的实现方法
在java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题. 通常对象之间的比较可以从两个方面去看: 第一个方面:对象的地址是否一样,也就是是否引用自同一个对象.这种方式可以直接使用& ...
- java null什么意思_浅谈java中null是什么,以及使用中要注意的事项
1.null既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型,你也可以将null转化成任何类型,例如: Integer i=null; Float f=null; String ...
- java 中的排序_浅谈java中常见的排序
浅谈java中常见的排序 学过java的人都知道,排序这一部分初次接触感觉还是有点难以理解,很多地方也会用到.然而,在java中常见的排序方法:冒泡排序,选择排序,插入排序等等.下面就让我们一起揭开他 ...
最新文章
- 超详细支持向量机知识点,面试官会问的都在这里了
- 黑马程序员—java基础总结1
- Ultra96_v2实现交通标示识别
- Android 人脸照片对比,人脸对比
- 怎么打败腾讯[纯讨论]
- centos7搭建git代码仓库
- JS与PHP向函数传递可变参数的区别
- 怎样让手机立马变空号?
- [Linux] C 语言遍历文件夹
- 学习scrapy使用
- 华为自带时钟天气下载_华为手机锁屏时钟软件
- PCB设计中常见的错误与解决方法
- EXFO max-715b光纤测试仪参数介绍
- iic调试软件上时钟芯片测试,硬件IIC测试成功!!给大家分享一下
- JVM源码分析之Metaspace解密
- Freetype学习笔记
- python画圆形螺旋线_【Python基础】利用 Python 搞定精美网络图!
- Spring源码学习(三)-- 底层架构核心概念解析
- vue 路由懒加载(延时加载、按需加载)
- 初始化磁盘选哪个格式 初始化磁盘分区形式选什么好
热门文章
- BearSkill之UIView挖空处理
- 360抢夺“度娘”?
- 安卓productFlavors多渠道打包简单使用
- (二进制枚举+思维)1625 夹克爷发红包
- 论文阅读-基于遗传算法的NAS
- 电脑一启动吃鸡就重启计算机,租号器登录电脑重启-租号玩绝地求生提示登录出现异请重启客户端...
- [CVPR2021-oral]Learning to Aggregate and Personalize 3D Face from In-the-Wild Photo Collection
- 使用百度云同步盘和Git Extensions进行代码托管
- 一杯茶一包烟,一行代码码一天!用Python分析程序员抽的烟!
- CRT显示器和液晶显示器