ABA问题

ABA问题

  • AtomicInteger

CAS --> UnSafe --> CAS底层思想 --> ABA -->原子引用更新 -->如何规避ABA问题

1.介绍

1.1图解

  • 所以,当A线程工作完成进行CAS时,预期值为A,真实值也为A,线程A会认为主物理内存中的值没有线程更改过,直接将其值写回主物理内存。
  • 实际上只是首尾相同,中间可能有很多次变更

1.2演示

import java.util.concurrent.atomic.AtomicReference;

public class ABADemo {
    static AtomicReference<Integer> atomicReference= new AtomicReference<>(100);
    public static  void main(String[] args){
        new Thread(() ->{
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        },"t1").start();
        new Thread(() ->{
            //暂停1s,保证t1完成了一次ABA操作
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println( atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());

        },"t2").start();
    }
}

2. 如何解决ABA问题

2.1 原子引用

import java.util.concurrent.atomic.AtomicReference;
class User{
    String userName;
    int age;

    public User(String userName, int age) {
        this.userName = userName;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}
public class AtomicReferenceDemo {
    public static void main(String[] args){
        AtomicReference<User> atomicReference = new AtomicReference<>();
        User z3 = new User("z3",22);
        User l4 = new User("l4",25);
        atomicReference.set(z3);
        System.out.println(atomicReference.compareAndSet(z3, l4)+"\t"+atomicReference.get().toString());
        System.out.println(atomicReference.compareAndSet(z3, l4)+"\t"+atomicReference.get().toString());
    }
}

2.2时间戳原子引用

//AtomicStampeReference
public class ABADemo {
    static AtomicReference<Integer> atomicReference= new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100, 1);
    public static  void main(String[] args){
        new Thread(() ->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t第一次版本号"+stamp);
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();}
            atomicStampedReference.compareAndSet(100,12,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t第二次版本号"+atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName()+"\t当前值"+atomicStampedReference.getReference());
            System.out.println(atomicStampedReference.compareAndSet(12,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1));
            System.out.println(Thread.currentThread().getName()+"\t第三次版本号"+atomicStampedReference.getStamp());
        },"t3").start();
        new Thread(() ->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t第一次版本号"+stamp);
            try{
                TimeUnit.SECONDS.sleep(5);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);
            System.out.println(Thread.currentThread().getName()+"\t修改"+result+"\t当前版本号"+atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName()+"\t当前值"+atomicStampedReference.getReference());
        },"t4").start();
    }
}

3.问题

  1. 当CAS传入的新值大于等于128的时候一直修改失败

    原因:Integer对-128~127间的数字有缓存,所以在此区间的current==expectedReference为true,当current>127,每次装箱将返回新的Integer对象,此时current!=expectedReference导致compareAndSet一直返回false。

posted @ 2022-02-20 15:45  ftfty  阅读(91)  评论(0编辑  收藏  举报