CAS导致的ABA问题及解决:时间戳原子引用AtomicReference、AtomicStampedReference

摘要:
例如,线程1从内存位置V获取A,而线程2也从V获取A。线程2执行一些操作将值更改为B,然后线程2将V的数据更改回A;此时,线程1执行CAS操作,发现内存中仍有A。然后线程1成功。虽然线程1的CAS操作成功,但这并不意味着进程正常。

1.CAS导致ABA问题:

CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并交换,那么在这个时间差中会导致数据的变化。

比如:线程1从内存位置V中取出A,这时线程2也从V中取出A,线程2进行了一些操作将值改成了B,然后线程2又将V的数据改回A;此时线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功。

尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的

解决ABA问题:利用原子引用+修改版本号(类似时间戳),每次需要获取到版本最新的值进行处理。

2.原子引用AtomicReference

ABA问题出现示例:

package com.mort.test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class TestAtomicReference {

    static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100);
    public static void main(String[] args) {
        new Thread(() -> {
            // 执行ABA操作
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "t1").start();

        new Thread(()->{
            // 暂停1秒钟t2线程,保证t1完成了一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"	" + atomicReference.get());
        }, "t2").start();
    }
}

3.解决ABA问题:时间戳原子引用AtomicStampedReference

代码示例:

package com.mort.test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class TestABASolve {
    //static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"	第1次版本号:"+stamp);
            // 暂停1秒钟t1线程,保证t2拿到版本号与t1相同
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 执行ABA操作
            atomicStampedReference.compareAndSet(100, 101,stamp,stamp+1);
            stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"	第2次版本号:"+stamp);
            atomicStampedReference.compareAndSet(101, 100, stamp,stamp+1);
//            System.out.println(Thread.currentThread().getName()+"	第3次版本号:"+atomicStampedReference.getStamp());
        }, "t1").start();

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"	第1次版本号:"+stamp);
            // 暂停3秒钟t2线程,保证t1完成了一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp+1);
            System.out.println(Thread.currentThread().getName()+"	执行结果:"+result+"	最新版本号:"+atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName()+"	当前实际最新值:"+atomicStampedReference.getReference());
        }, "t2").start();
    }
}

输出结果:

t1    第1次版本号:1
t2    第1次版本号:1
t1    第2次版本号:2
t2    执行结果:false    最新版本号:3
t2    当前实际最新值:100

免责声明:文章转载自《CAS导致的ABA问题及解决:时间戳原子引用AtomicReference、AtomicStampedReference》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇网页字体介绍以及网页常用字体设置MySQL实现批量检查表并进行repair与optimize的方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

__declspec的15种用法

__cdecl和__stdcall都是函数调用规范(还有一个__fastcall),规定了参数出入栈的顺序和方法,如果只用VC编程的话可以不用关心,但是要在C++和Pascal等其他语言通信的时候就要注意了,只有用相同的方法才能够调用成功.另外,像printf这样接受可变个数参数的函数只有用cdecl才能够实现.__declspec主要是用于说明DLL的引...

jvm内存模型和内存分配

1.什么是jvm? (1)jvm是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的。 (2)jvm包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域。 (3)JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改...

Android:子线程到底能不能更新UI?

问题由来 我们知道,Andoird由于修改UI是线程不安全的,只能在主线程中修改。如果多个线程修改UI肯定会花屏,于是谷歌做了限制,只能在主线程中修改UI。但是有次我在子线程中修改了UI没弹异常。 先来看两段代码 //正常运行btn1.setOnClickListener(new View.OnClickListener() { @Override...

jvm内存溢出性能调优

常用工具及命令 jps jstat Top jstack jmap mat工具  top -Hp pid可以查看某个进程的线程信息 -H 显示线程信息,-p指定pid jps:可以列出正在运行的虚拟机进程,并显示虚拟机执行主类名称及进程pid 如:jps -l pid Jstack命令 jstack是java虚拟机自带的一种堆栈跟踪工具。用于生成java虚...

java中终止线程的三种方式

在java中有三种方式可以终止线程。分别为:   1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。      2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。        3.  使用interrupt方法中断线程。 下面我们来详细的介绍这三种方...

斗鱼直播三面:说说JDK与JRE的区别是什么!

前言 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚...