Java面试题——ABA问题

从哪里引出的ABA问题

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

如果不理解什么是CAS和UnSafe问题,请跳入Java面试题——CAS(compareAndSet)

什么是ABA问题

通俗点讲就是,就和坐火车时买两头票差不多,线程只管开头和结尾的数据值一样就认为是正确的,中间被谁改过了,它并不知道。

举例说明:现在有两个线程,利用CAS原理来改变某一个共享变量的值,但是A线程在读取到主内存的值为10之后被挂起,B线程开始进行操作,B把主内存中的值改变成了15,之后B线程又把主内存中的值改回10,B线程结束,A线程开始运行,发现内存中的值还是10,认为是没有修改过的,但是实际上已经被B线程修改过。

ABA问题例子:

/**
 * @author shihangqi
 * @date 2019/10/16 - 20:50
 */
public class ABADemo {
     

    static AtomicReference<Integer> atomicReference = new AtomicReference<>();

    public static void main(String[] args) {
     
        new Thread(()->{
     
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();
        new Thread(()->{
     
            try {
     
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());
        },"t1").start();
    }


}

原子引用更新

其实就是Atomic类可以对自定义类进行包装。
实例代码:

/**
 * @author shihangqi
 * @date 2019/10/16 - 16:28
 */

@Getter
@ToString
@AllArgsConstructor
class User{
     
    String Username;
    int age;

}
public class AtomicReferenceDemo {
     

    public static void main(String[] args) {
     


        User z3 = new User("z3",22);
        User li4 = new User("li4",25);

        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(z3);

        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t "+atomicReference.get().toString());
        System.out.println(atomicReference.compareAndSet(z3, li4)+"\t "+atomicReference.get().toString());
    }
}

如何规避ABA问题

在其中加一种新的机制,就是修改版本号(类似时间戳)
假设A线程从主内存中拿到值为100 版本号为1,B线程也从主内存中拿到了值为100版本号为1,这时A线程挂起,B线程将值修改成了50,但是版本号要跟着修改成2,然后又进行了一次修改将50又改会了100,版本号3,这时B线程结束A线程启动,看到值为100,进行修改得到150但是版本号为2,低于当前版本号3,所以这次操作作废,需要重新去读取主内存中的值。

解决方法的类:使用AtomicStampedReference原子引用
实例代码:

/**
 * @author shihangqi
 * @date 2019/10/16 - 20:50
 */
public class ABADemo {
     
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);


    public static void main(String[] args) {
     
        System.out.println("===========这是ABA问题产生============");
        new Thread(()->{
     
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();
        new Thread(()->{
     
            try {
     
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 2019)+"\t"+atomicReference.get());
        },"t2").start();

        System.out.println("===========这是ABA问题的解决============");



        new Thread(()->{
     
            int stamp = atomicStampedReference.getStamp();
            System.out.println((Thread.currentThread().getName()+"\t第一次版本号:"+stamp));
            //暂停1秒钟t3线程
            try {
     TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e){
      e.printStackTrace(); }
            atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t第2次版本号:"+atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t第3次版本号:"+atomicStampedReference.getStamp());
        },"t3").start();

        new Thread(()->{
     
            int stamp = atomicStampedReference.getStamp();
            System.out.println((Thread.currentThread().getName()+"\t第一次版本号:"+stamp));
            //验证上面的t3线程完成了一次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()+"\t修改成功否:"+result+"\t当前最新实际版本号为:"+atomicStampedReference.getStamp());

            System.out.println(Thread.currentThread().getName()+"\t当前实际最新值:"+atomicStampedReference.getReference());
        },"t4").start();
    }


}






你可能感兴趣的:(面试集锦,ABA问题,如何解决CAS的缺点,CAS深度解析)