zoukankan      html  css  js  c++  java
  • ABA问题的产生及解决

    什么是ABA问题

    在CAS算法中,需要取出内存中某时刻的数据(由用户完成),在下一时刻比较并交换(CPU保证原子操作),这个时间差会导致数据的变化。

    1、线程1从内存位置V中取出A
    2、线程2从内存位置V中取出A
    3、线程2进行了写操作,将B写入内存位置V
    4、线程2将A再次写入内存位置V
    5、线程1进行CAS操作,发现V中仍然是A,交换成功

    尽管线程1的CAS操作成功,但线程1并不知道内存位置V的数据发生过改变

    ABA问题本质:

    根本在于CAS在修改变量的时候,无法记录变量的状态,比如是否修改过这个变量,修改的次数。

    public class ABADemo {
        
        private static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100);
    
        public static void main(String[] args) {
            new Thread(() -> {
                Integer a = atomicReference.get();
                atomicReference.compareAndSet(a, 101);
                atomicReference.compareAndSet(101, a);
                System.out.println("t1修改了atomicReference的值");
            },"t1").start();
            
            new Thread(() -> {
                Integer b = atomicReference.get();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(atomicReference.compareAndSet(b, 2019) + "	修改后的值:" + atomicReference.get());
            },"t2").start();
        }
    }

     ABA问题怎么解决

    public class AtomicStampedReferenceTest {
        
        private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1);
    
        public static void main(String[] args) {
            new Thread(() -> {
                Integer stamp = atomicStampedReference.getStamp();
                System.out.println("t1拿到的初始版本号:"  + stamp);
                //睡眠1秒,是为了让t2线程也拿到同样的初始版本号
                try {
                   Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                atomicStampedReference.compareAndSet(100, 101,stamp,stamp+1);
                stamp = atomicStampedReference.getStamp();
                atomicStampedReference.compareAndSet(101, 100,stamp,stamp+1);
            },"t1").start();
            
            new Thread(() -> {
                int stamp = atomicStampedReference.getStamp();
                System.out.println("t2拿到的初始版本号:" + stamp);
                //睡眠3秒,是为了让t1线程完成ABA操作
                try {
                    Thread.sleep(3000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                System.out.println("最新版本号:" + atomicStampedReference.getStamp());
                System.out.println(atomicStampedReference.compareAndSet(100, 2019,stamp,atomicStampedReference.getStamp() + 1) + "	当前 值:" + atomicStampedReference.getReference());
            },"t2").start();
        }
    }

    如果不关心引用变量更改了几次,只单纯的关心是否更改过,可以使用AtomicMarkableReference

    public class ABADemo {
        
        private static AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference<Integer>(100,false);
    
        public static void main(String[] args) {
            new Thread(() -> {
                System.out.println("t1版本号是否被更改:" + atomicMarkableReference.isMarked());
                //睡眠1秒,是为了让t2线程也拿到同样的初始版本号
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                atomicMarkableReference.compareAndSet(100, 101,false,true);
              
            },"t1").start();
            
            new Thread(() -> {
                boolean isMarked = atomicMarkableReference.isMarked();
                System.out.println("t2版本号是否被更改:" + isMarked);
                //睡眠3秒,是为了让t1线程执行
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("是否更改过:" + atomicMarkableReference.isMarked());
                System.out.println(atomicMarkableReference.compareAndSet(100, 2019,isMarked,true) + "	当前 值:" + atomicMarkableReference.getReference());
            },"t2").start();
        }
    }
  • 相关阅读:
    拖动内容,滚动条滚动,横向
    前端面试题及答案整理(一)
    微软拼音转换工具类ChnCharInfo.dll
    table表格,让thead固定,tbody有滚动条,关键是都对齐的纯css写法。
    [C#]_[使用微软OpenXmlSDK (OpenXmlReader)读取xlsx表格] 读取大数据量100万条数据Excel文件解决方案
    asp.net mvc Post上传文件大小限制
    .net OADate 转javascript的Datetime js 5位 日期 转换
    在.NET开发中的单元测试工具之(1)——NUnit
    在.NET开发中的单元测试工具之(2)——xUnit.Net
    Git 头像修改 原
  • 原文地址:https://www.cnblogs.com/moris5013/p/11824993.html
Copyright © 2011-2022 走看看