3.CAS引起的ABA问题以及原子引用

ABA以及原子更新引用

1 什么是ABA?

​ 一句话:狸猫换太子。

​ 详情:假如存在两个线程t1,t2,他们都将物理内存中的数据A复制到自己的工作内存中,t1的任务执行时间比较长,t2的执行时间短,t2将A改为B,然后又改回A,此时走人,t1回来之后发现符合CAS规则,然后将其修改为新的值==>尽管线程t1的cas操作成功,但是不代表这个过程就是没有问题的。

​ cas认为头和尾一致的时候,资源就没有被修改,过程是否修改是未知的,如果过程也不能被修改,就会出现问题。

解决方式:原子引用+时间戳。

2 原子引用

AtomicReferenceDemo:

package com.at.cas;

import jdk.nashorn.internal.objects.annotations.Getter;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author : code1997
 * @date :2020-09-2020/9/10 18:36
 */
    public static void main(String[] args) throws InterruptedException {
        User user1 = new User("张三", 22);
        User user2 = new User("李四", 23);
        AtomicReference<User> userAtomicReference = new AtomicReference<>();
        //原子变量user1
        userAtomicReference.set(user1);
        //true	User{userName='李四', age=23}
        System.out.println(userAtomicReference.compareAndSet(user1, user2)+"\t"+userAtomicReference.get());
        new Thread(){
            @Override
            public void run() {
                User user = userAtomicReference.get();
                user.setAge(66);
                userAtomicReference.compareAndSet(user1,user);
            }
        }.start();
        //等待上面的线程执行结束
        Thread.sleep(1000);
        //false	User{userName='李四', age=66}
        System.out.println(userAtomicReference.compareAndSet(user1, user2)+"\t"+userAtomicReference.get());
    }

class User{
    String userName;
    int age;

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}
3 时间戳+原子引用

之前我们了解到CAS存在ABA问题,AtomicStampedReference类。

官方描述:An {@code AtomicStampedReference} maintains an object reference along with an integer “stamp”, that can be updated atomically:维护对象引用以及可以自动更新的整数

ABADemo:

package com.at.cas;

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

/**
 * @author : code1997
 * @date :2020-09-2020/9/10 19:07
 */
public class ABADemo {
    static AtomicReference<Integer> integerAtomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> integerAtomicStampedReference=new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        System.out.println("==================1.ABA产生==================");
        new Thread(()->{
            integerAtomicReference.compareAndSet(100,101);
            integerAtomicReference.compareAndSet(101,100);
        },"t1").start();
        new Thread(()->{
            try {
                //暂停1s,等待t1完成ABA的操作。
                TimeUnit.SECONDS.sleep(1);
                //true	108   我们发现可以更新成功
                System.out.println(integerAtomicReference.compareAndSet(100, 108)+
                        "\t"+integerAtomicReference.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t2").start();
        try {
            //保证上面任务执行结束
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("=================2.ABA的解决=================");
        new Thread(()->{
            int stamp = integerAtomicStampedReference.getStamp();
            //t3第1次版本号为:1
            System.out.println(Thread.currentThread().getName()+"第1次版本号为:"+stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //进行ABA操作
            stamp=integerAtomicStampedReference.getStamp();
            //t3 true	2
            System.out.println(Thread.currentThread().getName()+"\t"+integerAtomicStampedReference.compareAndSet(100, 101,stamp, stamp + 1) +"\t"+integerAtomicStampedReference.getStamp());
            stamp=integerAtomicStampedReference.getStamp();
            //t3 true	3
            System.out.println(Thread.currentThread().getName()+"\t"+integerAtomicStampedReference.compareAndSet(101, 100,stamp, stamp + 1) +"\t"+integerAtomicStampedReference.getStamp());

        },"t3").start();
        new Thread(()->{
            int stamp = integerAtomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"第1次版本号为:"+stamp);
            try {
                //等待t3完成一次ABA操作
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //t4 false	3
            System.out.println(Thread.currentThread().getName()+"\t"+integerAtomicStampedReference.compareAndSet(100, 2020,stamp, stamp + 1) +"\t"+integerAtomicStampedReference.getStamp());
			//t4	当前实际的值:100
            System.out.println(Thread.currentThread().getName()+"\t当前实际的值:"+integerAtomicStampedReference.getReference());
        },"t4").start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值